From 75365e5aa48b9388f8c5c5c8980cb106e1fd4a87 Mon Sep 17 00:00:00 2001
From: Nils Christian Ehmke <nie@informatik.uni-kiel.de>
Date: Mon, 3 Dec 2012 14:51:57 +0100
Subject: [PATCH] Added a simple meta file to the projects; Added some
 parameters to the shell scripts (#642); Some code modifications for quality
 reasons

---
 .../.settings/org.eclipse.jdt.ui.prefs        |   2 +-
 Kieker.WebGUI/bin/Kieker.WebGUI.bat           |   5 +-
 Kieker.WebGUI/bin/Kieker.WebGUI.sh            |   2 +-
 .../bin/data/Advanced Example/meta.dat        |   3 +
 .../bin/data/Bookstore-Example/meta.dat       |   3 +
 .../common/layout/GraphFlowLayouter.java      |  41 +++----
 .../webgui/persistence/IProjectDAO.java       |  16 ++-
 .../persistence/impl/FSProjectDAOImpl.java    | 111 +++++++++++++++---
 .../impl/util/CloseableURLClassLoader.java    |  49 ++++----
 .../webgui/service/IProjectService.java       |  15 ++-
 .../service/impl/ProjectServiceImpl.java      |  13 +-
 .../web/beans/application/ProjectsBean.java   |  23 +++-
 .../dialogs/ProjectOverviewPageDialogs.xhtml  |   2 +-
 .../webapp/pages/ProjectOverviewPage.xhtml    |   2 +-
 14 files changed, 216 insertions(+), 71 deletions(-)
 create mode 100644 Kieker.WebGUI/bin/data/Advanced Example/meta.dat
 create mode 100644 Kieker.WebGUI/bin/data/Bookstore-Example/meta.dat

diff --git a/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs b/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs
index 9961c8b4..85c1f324 100644
--- a/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs
+++ b/Kieker.WebGUI/.settings/org.eclipse.jdt.ui.prefs
@@ -58,7 +58,7 @@ formatter_settings_version=12
 org.eclipse.jdt.ui.exception.name=ex
 org.eclipse.jdt.ui.gettersetter.use.is=true
 org.eclipse.jdt.ui.ignorelowercasenames=true
-org.eclipse.jdt.ui.importorder=java;javax;junit;org;com;kieker;org.primefaces;org.eclipse;
+org.eclipse.jdt.ui.importorder=java;javax;junit;org;com;kieker;org.primefaces;org.eclipse;de.cau.cs.kieler;
 org.eclipse.jdt.ui.keywordthis=false
 org.eclipse.jdt.ui.ondemandthreshold=99
 org.eclipse.jdt.ui.overrideannotation=true
diff --git a/Kieker.WebGUI/bin/Kieker.WebGUI.bat b/Kieker.WebGUI/bin/Kieker.WebGUI.bat
index a8a11623..62e25701 100644
--- a/Kieker.WebGUI/bin/Kieker.WebGUI.bat
+++ b/Kieker.WebGUI/bin/Kieker.WebGUI.bat
@@ -1,6 +1,7 @@
 @echo off 
 REM @author Nils Christian Ehmke
 
-java -XX:PermSize=256M -XX:MaxPermSize=512M -Xms128M -Xmx256M  -jar ..\lib\jetty-runner-*.jar --path /Kieker.WebGUI ..\target\Kieker.WebGUI-*.war
+java -XX:PermSize=256M -XX:MaxPermSize=512M -Xms128M -Xmx256M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled -jar ..\lib\jetty-runner-*.jar --path /Kieker.WebGUI ..\target\Kieker.WebGUI-*.war
+
+@echo on
 
-@echo on
\ No newline at end of file
diff --git a/Kieker.WebGUI/bin/Kieker.WebGUI.sh b/Kieker.WebGUI/bin/Kieker.WebGUI.sh
index 511e8516..fc2ebe5e 100644
--- a/Kieker.WebGUI/bin/Kieker.WebGUI.sh
+++ b/Kieker.WebGUI/bin/Kieker.WebGUI.sh
@@ -2,4 +2,4 @@
 
 # @author Nils Christian Ehmke
 
-java -XX:PermSize=256M -XX:MaxPermSize=512M -Xms128M -Xmx256M  -jar ../lib/jetty-runner-*.jar --path /Kieker.WebGUI ../target/Kieker.WebGUI-*.war
\ No newline at end of file
+java -XX:PermSize=256M -XX:MaxPermSize=512M -Xms128M -Xmx256M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled   -jar ../lib/jetty-runner-*.jar --path /Kieker.WebGUI ../target/Kieker.WebGUI-*.war
\ No newline at end of file
diff --git a/Kieker.WebGUI/bin/data/Advanced Example/meta.dat b/Kieker.WebGUI/bin/data/Advanced Example/meta.dat
new file mode 100644
index 00000000..ea9e2b79
--- /dev/null
+++ b/Kieker.WebGUI/bin/data/Advanced Example/meta.dat	
@@ -0,0 +1,3 @@
+#
+#Mon Dec 03 14:49:14 CET 2012
+owner=Kieker-Administrator
diff --git a/Kieker.WebGUI/bin/data/Bookstore-Example/meta.dat b/Kieker.WebGUI/bin/data/Bookstore-Example/meta.dat
new file mode 100644
index 00000000..ea9e2b79
--- /dev/null
+++ b/Kieker.WebGUI/bin/data/Bookstore-Example/meta.dat
@@ -0,0 +1,3 @@
+#
+#Mon Dec 03 14:49:14 CET 2012
+owner=Kieker-Administrator
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/common/layout/GraphFlowLayouter.java b/Kieker.WebGUI/src/main/java/kieker/webgui/common/layout/GraphFlowLayouter.java
index 44edc884..c233fd10 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/common/layout/GraphFlowLayouter.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/common/layout/GraphFlowLayouter.java
@@ -19,9 +19,15 @@ package kieker.webgui.common.layout;
 import java.util.ArrayList;
 import java.util.List;
 
+import kieker.webgui.common.exception.InvalidInputSizeException;
+import kieker.webgui.common.exception.UninitializedGraphException;
+
+import org.eclipse.emf.common.util.EList;
+
 import de.cau.cs.kieler.core.alg.BasicProgressMonitor;
 import de.cau.cs.kieler.core.alg.IKielerProgressMonitor;
 import de.cau.cs.kieler.core.kgraph.KEdge;
+import de.cau.cs.kieler.core.kgraph.KLabeledGraphElement;
 import de.cau.cs.kieler.core.kgraph.KNode;
 import de.cau.cs.kieler.core.kgraph.KPort;
 import de.cau.cs.kieler.kiml.AbstractLayoutProvider;
@@ -34,11 +40,6 @@ import de.cau.cs.kieler.kiml.options.PortConstraints;
 import de.cau.cs.kieler.kiml.util.KimlUtil;
 import de.cau.cs.kieler.klay.layered.LayeredLayoutProvider;
 
-import kieker.webgui.common.exception.InvalidInputSizeException;
-import kieker.webgui.common.exception.UninitializedGraphException;
-
-import org.eclipse.emf.common.util.EList;
-
 /**
  * This class provides a single method, that is used for GraphFlow layouting. The JavaScript GraphFlow throws an "autoLayout"-Event upon using the function
  * autoLayout(). This event provides two Strings which may be used by this class' layoutGraph(nodes, edges) method. The return value can then be used as an argument
@@ -69,7 +70,7 @@ public final class GraphFlowLayouter {
 		layoutInformation.getGraph().getData(KShapeLayout.class).setProperty(LayoutOptions.EDGE_ROUTING, EdgeRouting.ORTHOGONAL);
 
 		// prepare array of lists
-		List<Object> child;
+		List<KLabeledGraphElement> child;
 
 		final int dimHalf = Integer.parseInt(positions[0]);
 		final int dim = dimHalf * 2;
@@ -101,8 +102,8 @@ public final class GraphFlowLayouter {
 			node = KimlUtil.createInitializedNode();
 			node.setParent(layoutInformation.getGraph());
 
-			child = new ArrayList<Object>();
-			layoutInformation.getChildren()[i] = child;
+			child = new ArrayList<KLabeledGraphElement>();
+			layoutInformation.getChildren().add(i, child);
 			child.add(node);
 
 			// set node dimensions
@@ -190,13 +191,13 @@ public final class GraphFlowLayouter {
 			// read from String
 			sourceID = Integer.parseInt(edgeInfo[e++]);
 			targetID = Integer.parseInt(edgeInfo[e++]);
-			sourcePort = (KPort) layoutInformation.getChildren()[sourceID].get(1 + Integer.parseInt(edgeInfo[e++]));
-			targetPort = (KPort) layoutInformation.getChildren()[targetID].get(1 + Integer.parseInt(edgeInfo[e++]));
+			sourcePort = (KPort) layoutInformation.getChildren().get(sourceID).get(1 + Integer.parseInt(edgeInfo[e++]));
+			targetPort = (KPort) layoutInformation.getChildren().get(targetID).get(1 + Integer.parseInt(edgeInfo[e++]));
 
 			// add edge to graph
 			edge = KimlUtil.createInitializedEdge();
-			edge.setSource((KNode) layoutInformation.getChildren()[sourceID].get(0));
-			edge.setTarget((KNode) layoutInformation.getChildren()[targetID].get(0));
+			edge.setSource((KNode) layoutInformation.getChildren().get(sourceID).get(0));
+			edge.setTarget((KNode) layoutInformation.getChildren().get(targetID).get(0));
 
 			// set ports of edge
 			edge.setSourcePort(sourcePort);
@@ -204,7 +205,7 @@ public final class GraphFlowLayouter {
 			edge.setTargetPort(targetPort);
 			targetPort.getEdges().add(edge);
 
-			layoutInformation.getEdges()[ea++] = edge;
+			layoutInformation.getEdges().add(ea++, edge);
 		}
 
 	}
@@ -221,8 +222,8 @@ public final class GraphFlowLayouter {
 	private static LayoutInformation assembleLayoutInformation(final String nodesStr, final String edgesStr) {
 		final String[] edgeInfo = edgesStr.split(" ");
 		final String[] positions = nodesStr.split(" ");
-		final List<Object>[] children = new ArrayList[positions.length / 2];
-		final KEdge[] edges = new KEdge[edgeInfo.length / 4];
+		final List<List<KLabeledGraphElement>> children = new ArrayList<List<KLabeledGraphElement>>(positions.length / 2);
+		final List<KEdge> edges = new ArrayList<KEdge>(edgeInfo.length / 4);
 		final LayoutInformation layoutInformation = new LayoutInformation(KimlUtil.createInitializedNode(), children, edges);
 
 		GraphFlowLayouter.addNodes(positions, layoutInformation);
@@ -328,11 +329,11 @@ public final class GraphFlowLayouter {
 		/**
 		 * An array containing the child node and the ports from left to right.
 		 */
-		private final List<Object>[] children;
+		private final List<List<KLabeledGraphElement>> children;
 		/**
 		 * An array containing edges. This way we can return edge information in the same order.
 		 */
-		private final KEdge[] edges;
+		private final List<KEdge> edges;
 
 		/**
 		 * Creates a new instance of this class.
@@ -344,7 +345,7 @@ public final class GraphFlowLayouter {
 		 * @param edges
 		 *            The edges of the graph.
 		 */
-		public LayoutInformation(final KNode graph, final List<Object>[] children, final KEdge[] edges) {
+		public LayoutInformation(final KNode graph, final List<List<KLabeledGraphElement>> children, final List<KEdge> edges) {
 			this.graph = graph;
 			this.children = children;
 			this.edges = edges;
@@ -364,7 +365,7 @@ public final class GraphFlowLayouter {
 		 * 
 		 * @return The current value of the property.
 		 */
-		public List<Object>[] getChildren() {
+		public List<List<KLabeledGraphElement>> getChildren() {
 			return this.children;
 		}
 
@@ -373,7 +374,7 @@ public final class GraphFlowLayouter {
 		 * 
 		 * @return The current value of the property.
 		 */
-		public KEdge[] getEdges() {
+		public List<KEdge> getEdges() {
 			return this.edges;
 		}
 
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java
index cb2f9839..b0612cbc 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/IProjectDAO.java
@@ -45,13 +45,15 @@ public interface IProjectDAO {
 	 * 
 	 * @param projectName
 	 *            The name of the new project.
+	 * @param username
+	 *            The name of the user who created the project.
 	 * @throws ProjectAlreadyExistingException
 	 *             If a project with the same name exists already.
 	 * @throws IOException
 	 *             If something went wrong during the creation of the project.
 	 */
 	@PreAuthorize("hasAnyRole('User', 'Administrator')")
-	public abstract void addProject(String projectName) throws ProjectAlreadyExistingException, IOException;
+	public abstract void addProject(String projectName, final String username) throws ProjectAlreadyExistingException, IOException;
 
 	/**
 	 * This method makes a copy of a project and saves it under another name. If the method fails due to an {@link IOException}, it will make sure that the
@@ -223,6 +225,16 @@ public interface IProjectDAO {
 	 * @return An object containing the available components as model instances.
 	 */
 	@PreAuthorize("isAuthenticated()")
-	public abstract ComponentListContainer getAvailableComponents(String project);
+	public abstract ComponentListContainer getAvailableComponents(final String project);
+
+	/**
+	 * Delivers the owner of the given project or a substituion if the meta data are corrupt or missing.
+	 * 
+	 * @param projectName
+	 *            The name of the project whose owner should be delivered.
+	 * @return The owner (creator) of the project.
+	 */
+	@PreAuthorize("isAuthenticated()")
+	public abstract String getOwner(final String projectName);
 
 }
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java
index d577e054..88afa664 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/FSProjectDAOImpl.java
@@ -19,9 +19,11 @@ package kieker.webgui.persistence.impl;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.ref.WeakReference;
 import java.lang.reflect.InvocationTargetException;
@@ -39,6 +41,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.annotation.PostConstruct;
@@ -97,6 +100,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 
 	private static final MIAnalysisMetaModelFactory FACTORY = MIAnalysisMetaModelFactory.eINSTANCE;
 	private static final String KIEKER_LIB = "kieker-1.6_emf.jar";
+	private static final String META_FILE = "meta.dat";
 	private static final String KAX_EXTENSION = "kax";
 	private static final String LIB_EXTENSION = "jar";
 	private static final String LIB_DIRECTORY = "lib";
@@ -156,7 +160,9 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	 */
 	private void initializeAvailableComponents(final String project) throws IOException {
 		try {
-			final CloseableURLClassLoader classLoader = (CloseableURLClassLoader) this.getClassLoader(project, this);
+			final Object dummyObject = new Object();
+			// Deliver a dummy object as a requester to make sure that the classloader can be disposed. Once the program exits this scope, it can be released.
+			final CloseableURLClassLoader classLoader = (CloseableURLClassLoader) this.getClassLoader(project, dummyObject);
 			final ClassAndMethodContainer classAndMethodContainer = new ClassAndMethodContainer(classLoader);
 
 			final List<PluginContainer> readers = new ArrayList<PluginContainer>();
@@ -781,10 +787,11 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	 */
 	@Override
 	@PreAuthorize("hasAnyRole('User', 'Administrator')")
-	public void addProject(final String projectName) throws ProjectAlreadyExistingException, IOException {
+	public void addProject(final String projectName, final String username) throws ProjectAlreadyExistingException, IOException {
 		// Assemble all necessary paths and files for the given project
-		final File projectDir = this.assembleProjectDir(projectName);
-		final File projectFile = this.assembleKaxFile(projectName);
+		final File projectDir = FSProjectDAOImpl.assembleProjectDir(projectName);
+		final File projectFile = FSProjectDAOImpl.assembleKaxFile(projectName);
+		final File metaFile = FSProjectDAOImpl.assembleMetaFile(projectName);
 		final File libDir = this.assembleLibDir(projectName);
 
 		// We need an "empty" project in order to save it.
@@ -796,11 +803,25 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 		}
 
 		try {
-			// Try to create the directories
-			if (projectDir.mkdir() && libDir.mkdir()) {
+			// Try to create the directories and files
+			if (projectDir.mkdir() && libDir.mkdir() && metaFile.createNewFile()) {
 				// Try to save the file
 				AnalysisController.saveToFile(projectFile, emptyProject);
 				this.initializeAvailableComponents(projectName);
+
+				// Store the initial meta data
+				final Properties properties = new Properties();
+				properties.put("owner", username);
+
+				FileOutputStream out = null;
+				try {
+					out = new FileOutputStream(metaFile);
+					properties.store(out, "");
+				} finally {
+					if (out != null) {
+						out.close();
+					}
+				}
 			} else {
 				// The directories could not be created
 				throw new IOException("Project-Directories could not be created.");
@@ -831,7 +852,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	public void copyProject(final String originalProjectName, final String newProjectName) throws ProjectNotExistingException, ProjectAlreadyExistingException,
 			IOException {
 		// Get the necessary paths
-		final File dstProjDir = this.assembleProjectDir(newProjectName);
+		final File dstProjDir = FSProjectDAOImpl.assembleProjectDir(newProjectName);
 		final File srcLibDir = this.assembleLibDir(originalProjectName);
 		final File dstLibDir = this.assembleLibDir(newProjectName);
 
@@ -840,8 +861,8 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 			throw new ProjectAlreadyExistingException("A project with the name '" + newProjectName + "' exists already.");
 		}
 
-		final File srcKaxFile = this.assembleKaxFile(originalProjectName);
-		final File dstKaxFile = this.assembleKaxFile(newProjectName);
+		final File srcKaxFile = FSProjectDAOImpl.assembleKaxFile(originalProjectName);
+		final File dstKaxFile = FSProjectDAOImpl.assembleKaxFile(newProjectName);
 
 		try {
 			if (dstProjDir.mkdir() && dstLibDir.mkdir()) {
@@ -886,7 +907,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 
 		try {
 			// Load the project
-			return AnalysisController.loadFromFile(this.assembleKaxFile(projectName).getAbsoluteFile());
+			return AnalysisController.loadFromFile(FSProjectDAOImpl.assembleKaxFile(projectName).getAbsoluteFile());
 		} catch (final FileNotFoundException ex) {
 			throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist.", ex);
 		}
@@ -906,7 +927,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 
 		// Load the project
 		final Object project = ClassAndMethodContainer.invokeClassMethod(classAndMethodContainer.getAnalysisControllerLoadFromFile(), null,
-				this.assembleKaxFile(projectName));
+				FSProjectDAOImpl.assembleKaxFile(projectName));
 
 		if (project == null) {
 			throw new IOException("Project could not be loaded.");
@@ -936,7 +957,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 		}
 
 		// Try to save it.
-		AnalysisController.saveToFile(this.assembleKaxFile(projectName), project);
+		AnalysisController.saveToFile(FSProjectDAOImpl.assembleKaxFile(projectName), project);
 	}
 
 	/*
@@ -951,7 +972,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 		if (!this.projectExists(projectName)) {
 			throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist.");
 		}
-		return this.assembleKaxFile(projectName).lastModified();
+		return FSProjectDAOImpl.assembleKaxFile(projectName).lastModified();
 	}
 
 	/*
@@ -1090,6 +1111,11 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 		return result;
 	}
 
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see kieker.webgui.persistence.IProjectDAO#getAvailableComponents(java.lang.String)
+	 */
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public ComponentListContainer getAvailableComponents(final String project) {
@@ -1104,9 +1130,20 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	 * @return true if and only if a directory with the name of the project exists in the root dir.
 	 */
 	private boolean projectExists(final String projectName) {
-		return this.assembleKaxFile(projectName).exists();
+		return FSProjectDAOImpl.assembleKaxFile(projectName).exists();
 	}
 
+	/**
+	 * Delivers the url of the given library to the given project.
+	 * 
+	 * @param lib
+	 *            The library of the project.
+	 * @param project
+	 *            The project itself.
+	 * @return An URL pointing to the URL.
+	 * @throws MalformedURLException
+	 *             If something went wrong.
+	 */
 	private URL getURL(final String lib, final String project) throws MalformedURLException {
 		final File file = new File(FSProjectDAOImpl.ROOT_DIRECTORY + File.separator + project + File.separator + FSProjectDAOImpl.LIB_DIRECTORY + File.separator
 				+ lib);
@@ -1120,7 +1157,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	 *            The name of the project.
 	 * @return The directory of the project.
 	 */
-	private File assembleProjectDir(final String projectName) {
+	private static File assembleProjectDir(final String projectName) {
 		return new File(FSProjectDAOImpl.ROOT_DIRECTORY + File.separator + projectName);
 	}
 
@@ -1131,10 +1168,21 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	 *            The name of the project.
 	 * @return The kax-file of the project.
 	 */
-	private File assembleKaxFile(final String projectName) {
+	private static File assembleKaxFile(final String projectName) {
 		return new File(FSProjectDAOImpl.ROOT_DIRECTORY + File.separator + projectName + File.separator + projectName + "." + FSProjectDAOImpl.KAX_EXTENSION);
 	}
 
+	/**
+	 * Assembles the {@link File}-element pointing to the meta file of the given project.
+	 * 
+	 * @param projectName
+	 *            The name of the project.
+	 * @return The meta file of the project.
+	 */
+	private static File assembleMetaFile(final String projectName) {
+		return new File(FSProjectDAOImpl.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSProjectDAOImpl.META_FILE);
+	}
+
 	/**
 	 * Assembles the {@link File}-element pointing to the library directory of the given project.
 	 * 
@@ -1180,7 +1228,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	@Override
 	@PreAuthorize("isAuthenticated()")
 	public File getProjectFile(final String projectName) {
-		return this.assembleKaxFile(projectName);
+		return FSProjectDAOImpl.assembleKaxFile(projectName);
 	}
 
 	/*
@@ -1232,6 +1280,35 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 		}
 	}
 
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see kieker.webgui.persistence.IProjectDAO#getOwner(java.lang.String)
+	 */
+	@Override
+	public String getOwner(final String projectName) {
+		final File metaFile = FSProjectDAOImpl.assembleMetaFile(projectName);
+		final Properties properties = new Properties();
+		InputStream inputStream = null;
+		try {
+			inputStream = new FileInputStream(metaFile);
+			properties.load(inputStream);
+			return properties.getProperty("owner");
+		} catch (final IOException ex) {
+			FSProjectDAOImpl.LOG.warn("Could not open meta file.", ex);
+		} finally {
+			try {
+				if (inputStream != null) {
+					inputStream.close();
+				}
+			} catch (final IOException ex) {
+				FSProjectDAOImpl.LOG.warn("Could not open meta file.", ex);
+			}
+		}
+
+		return "N/A";
+	}
+
 	/**
 	 * This helper class is responsible for creating a classloader as a privileged action. This is recommended due to the java security manager.
 	 * 
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/CloseableURLClassLoader.java b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/CloseableURLClassLoader.java
index e20604da..91086e00 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/CloseableURLClassLoader.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/persistence/impl/util/CloseableURLClassLoader.java
@@ -30,6 +30,8 @@ import java.util.Collection;
  */
 public class CloseableURLClassLoader extends URLClassLoader implements Closeable {
 
+	private boolean closed = false;
+
 	/**
 	 * Creates a new instance of this class using the given parameters.
 	 * 
@@ -48,31 +50,36 @@ public class CloseableURLClassLoader extends URLClassLoader implements Closeable
 	 * @see java.io.Closeable#close()
 	 */
 	@Override
-	public void close() throws IOException {
-		try {
-			final Class<URLClassLoader> clazz = URLClassLoader.class;
-			final java.lang.reflect.Field ucp = clazz.getDeclaredField("ucp");
-			ucp.setAccessible(true);
+	public synchronized void close() throws IOException {
+		// Make sure that the classloader can only be closed once.
+		if (!this.closed) {
+			this.closed = true;
+
+			try {
+				final Class<URLClassLoader> clazz = URLClassLoader.class;
+				final java.lang.reflect.Field ucp = clazz.getDeclaredField("ucp");
+				ucp.setAccessible(true);
 
-			final Object sunMiscURLClassPath = ucp.get(this);
-			final java.lang.reflect.Field loaders = sunMiscURLClassPath.getClass().getDeclaredField("loaders");
-			loaders.setAccessible(true);
+				final Object sunMiscURLClassPath = ucp.get(this);
+				final java.lang.reflect.Field loaders = sunMiscURLClassPath.getClass().getDeclaredField("loaders");
+				loaders.setAccessible(true);
 
-			// Run through all available loaders and try to close them
-			final Object javaUtilCollection = loaders.get(sunMiscURLClassPath);
-			for (final Object sunMiscURLClassPathJarLoader : ((Collection<?>) javaUtilCollection).toArray()) {
-				try {
-					final java.lang.reflect.Field loader = sunMiscURLClassPathJarLoader.getClass().getDeclaredField("jar");
-					loader.setAccessible(true);
-					final Object javaUtilIarJarFile = loader.get(sunMiscURLClassPathJarLoader);
-					((java.util.jar.JarFile) javaUtilIarJarFile).close();
-				} catch (final Throwable t) { // NOCS, NOPMD (Catch of Throwable)
-					// if we got this far, this is probably not a JAR loader so skip it
+				// Run through all available loaders and try to close them
+				final Object javaUtilCollection = loaders.get(sunMiscURLClassPath);
+				for (final Object sunMiscURLClassPathJarLoader : ((Collection<?>) javaUtilCollection).toArray()) {
+					try {
+						final java.lang.reflect.Field loader = sunMiscURLClassPathJarLoader.getClass().getDeclaredField("jar");
+						loader.setAccessible(true);
+						final Object javaUtilIarJarFile = loader.get(sunMiscURLClassPathJarLoader);
+						((java.util.jar.JarFile) javaUtilIarJarFile).close();
+					} catch (final Throwable t) { // NOCS, NOPMD (Catch of Throwable)
+						// if we got this far, this is probably not a JAR loader so skip it
+					}
 				}
+			} catch (final Throwable ex) { // NOCS, NOPMD (Catch of Throwable)
+				// probably not a SUN VM
+				throw new IOException("Not a Sun VM.", ex);
 			}
-		} catch (final Throwable ex) { // NOCS, NOPMD (Catch of Throwable)
-			// probably not a SUN VM
-			throw new IOException("Not a Sun VM.", ex);
 		}
 	}
 
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java
index 5b441bd9..7e4d7f9e 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/IProjectService.java
@@ -48,12 +48,14 @@ public interface IProjectService {
 	 * 
 	 * @param projectName
 	 *            The name of the new project.
+	 * @param username
+	 *            The name of the user who created the project.
 	 * @throws ProjectAlreadyExistingException
 	 *             If a project with the same name exists already.
 	 * @throws IOException
 	 *             If something went wrong during the creation of the project.
 	 */
-	public void addProject(final String projectName) throws ProjectAlreadyExistingException, IOException;
+	public void addProject(final String projectName, final String username) throws ProjectAlreadyExistingException, IOException;
 
 	/**
 	 * This method makes a copy of a project and saves it under another name. If the method fails due to an {@link IOException}, it will make sure that the
@@ -137,6 +139,17 @@ public interface IProjectService {
 	 */
 	public long getCurrTimeStamp(final String projectName) throws ProjectNotExistingException;
 
+	/**
+	 * Delivers the owner of the given project or a substituion if the meta data are corrupt or missing.
+	 * 
+	 * @param projectName
+	 *            The name of the project whose owner should be delivered.
+	 * @return The owner (creator) of the project.
+	 * @throws ProjectNotExistingException
+	 *             If a project with the given name does not exist.
+	 */
+	public String getOwner(final String projectName) throws ProjectNotExistingException;
+
 	/**
 	 * This method tries to upload a dependency to the given project. An existing version of the library will be overwritten.
 	 * 
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java
index 3ef50786..568abefd 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/service/impl/ProjectServiceImpl.java
@@ -63,11 +63,11 @@ public final class ProjectServiceImpl implements IProjectService {
 	}
 
 	@Override
-	public void addProject(final String projectName) throws ProjectAlreadyExistingException, IOException {
+	public void addProject(final String projectName, final String username) throws ProjectAlreadyExistingException, IOException {
 		final Object projectLock = this.getLock(projectName, this.fileSystemLocks);
 
 		synchronized (projectLock) {
-			this.projectDAO.addProject(projectName);
+			this.projectDAO.addProject(projectName, username);
 		}
 	}
 
@@ -283,4 +283,13 @@ public final class ProjectServiceImpl implements IProjectService {
 			return this.projectDAO.deleteLibrary(projectName, libName);
 		}
 	}
+
+	@Override
+	public String getOwner(final String projectName) throws ProjectNotExistingException {
+		final Object projectLock = this.getLock(projectName, this.fileSystemLocks);
+
+		synchronized (projectLock) {
+			return this.projectDAO.getOwner(projectName);
+		}
+	}
 }
diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/ProjectsBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/ProjectsBean.java
index 88b7f9e1..6afa88c1 100644
--- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/ProjectsBean.java
+++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/application/ProjectsBean.java
@@ -37,6 +37,7 @@ import kieker.webgui.common.exception.ProjectAlreadyExistingException;
 import kieker.webgui.common.exception.ProjectLoadException;
 import kieker.webgui.common.exception.ProjectNotExistingException;
 import kieker.webgui.service.IProjectService;
+import kieker.webgui.web.beans.session.UserBean;
 import kieker.webgui.web.beans.view.CurrentProjectOverviewBean;
 
 /**
@@ -88,11 +89,13 @@ public final class ProjectsBean {
 	 *            The name for the new project which should be added to the application.
 	 * @param currentProjectOverviewBean
 	 *            The current instance of {@link CurrentProjectOverviewBean} which will be used to update the list of projects.
+	 * @param userBean
+	 *            The current instance of {@link UserBean} which will be used to get the name of the user.
 	 */
-	public void addProject(final String project, final CurrentProjectOverviewBean currentProjectOverviewBean) {
+	public void addProject(final String project, final CurrentProjectOverviewBean currentProjectOverviewBean, final UserBean userBean) {
 		try {
 			// Try and use the FS-Manager to create the project atomically.
-			this.projectService.addProject(project);
+			this.projectService.addProject(project, userBean.getUsername());
 			// If there were no exception, everything went well. We can add the project to our list.
 			this.projects.add(project);
 			// Inform the user
@@ -186,7 +189,23 @@ public final class ProjectsBean {
 			// We can assume that something went wrong
 			return ProjectsBean.DEFAULT_TIMESTAMP;
 		}
+	}
 
+	/**
+	 * Delivers the owner of the given project or a substituion if the meta data are corrupt or missing.
+	 * 
+	 * @param project
+	 *            The name of the project whose owner should be delivered.
+	 * @return The owner (creator) of the project.
+	 */
+	public String getOwner(final String project) {
+		try {
+			return this.projectService.getOwner(project);
+		} catch (final ProjectNotExistingException ex) {
+			ProjectsBean.LOG.info("A project with the given name does not exist.", ex);
+			// We can assume that something went wrong
+			return "N/A";
+		}
 	}
 
 	/**
diff --git a/Kieker.WebGUI/src/main/webapp/dialogs/ProjectOverviewPageDialogs.xhtml b/Kieker.WebGUI/src/main/webapp/dialogs/ProjectOverviewPageDialogs.xhtml
index 25ae3ba6..efe32748 100644
--- a/Kieker.WebGUI/src/main/webapp/dialogs/ProjectOverviewPageDialogs.xhtml
+++ b/Kieker.WebGUI/src/main/webapp/dialogs/ProjectOverviewPageDialogs.xhtml
@@ -18,7 +18,7 @@
 
             <hr/>
             <div style="text-align: right">
-                <p:commandButton value="#{localizedMessages.ok}" action="#{projectsBean.addProject(stringBean.string, currentProjectOverviewBean)}" update=":projectsListForm :messages" oncomplete="newProjectDialog.hide()" />
+                <p:commandButton value="#{localizedMessages.ok}" action="#{projectsBean.addProject(stringBean.string, currentProjectOverviewBean, userBean)}" update=":projectsListForm :messages" oncomplete="newProjectDialog.hide()" />
             </div>
         </h:form>  
     </p:dialog>
diff --git a/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml
index 4c60dc7a..65259561 100644
--- a/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml
+++ b/Kieker.WebGUI/src/main/webapp/pages/ProjectOverviewPage.xhtml
@@ -79,7 +79,7 @@
                         </p:column>
 
                         <p:column headerText="#{localizedProjectOverviewMessages.owner}" style="text-align: center">   
-                            <h:outputText value="N/A" />  
+                            <h:outputText value="#{projectsBean.getOwner(project)}" />  
                         </p:column>  
 
                         <p:column headerText="#{localizedProjectOverviewMessages.lastModification}" sortBy="#{projectsBean.getCurrTimeStamp(project)}" style="text-align: center">  
-- 
GitLab