From 18c0eefc795e35eceb276e4807a38130c4d7c3d3 Mon Sep 17 00:00:00 2001
From: Nils Christian Ehmke <nie@informatik.uni-kiel.de>
Date: Sat, 3 Nov 2012 11:20:29 +0100
Subject: [PATCH] Classloader issue

---
 .../persistence/impl/FSProjectDAOImpl.java    | 76 +++++++++++++++----
 1 file changed, 61 insertions(+), 15 deletions(-)

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 e13bd892..d15bcc71 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
@@ -38,9 +38,12 @@ import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
 
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Service;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.util.FileSystemUtils;
 import org.springframework.util.WeakReferenceMonitor;
 import org.springframework.util.WeakReferenceMonitor.ReleaseListener;
 
@@ -75,6 +78,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	private static final String LIB_EXTENSION = "jar";
 	private static final String LIB_DIRECTORY = "lib";
 	private static final String ROOT_DIRECTORY = "data";
+	private static final String TEMP_DIRECTORY = "temp";
 	/**
 	 * The library for kieker which is contained in the war-file as a resource.
 	 */
@@ -86,6 +90,7 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 
 	private final MIAnalysisMetaModelFactory factory = new MAnalysisMetaModelFactory();
 	private final Map<CloseableURLClassLoader, WeakReference<Object>> classLoaders = new ConcurrentHashMap<CloseableURLClassLoader, WeakReference<Object>>();
+	private final Map<File, WeakReference<Object>> tempDirs = new ConcurrentHashMap<File, WeakReference<Object>>();
 
 	/**
 	 * Default constructor. <b>Do not use this constructor. This bean is Spring managed.</b>
@@ -102,13 +107,22 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 	 */
 	@PostConstruct
 	public void initialize() throws IOException {
-		// Check for our root-directory and create it if necessary
-		final File rootDir = new File(FSProjectDAOImpl.ROOT_DIRECTORY);
-		if (!rootDir.exists()) {
-			final boolean result = rootDir.mkdir();
-			if (!result) {
-				throw new IOException("Could not create root directory.");
-			}
+		// Check for the necessary directories and create them if necessary
+		this.checkDir(FSProjectDAOImpl.ROOT_DIRECTORY);
+		this.checkDir(FSProjectDAOImpl.TEMP_DIRECTORY);
+	}
+
+	@PreDestroy
+	public void destroy() throws IOException {
+		// Try to clear the temporary directory
+		FileSystemUtils.deleteRecursively(new File(FSProjectDAOImpl.TEMP_DIRECTORY));
+	}
+
+	private void checkDir(final String dirName) throws IOException {
+		final File dir = new File(dirName);
+		if (!dir.exists() && !dir.mkdir()) {
+			// Try to create the directory
+			throw new IOException("Could not create directory '" + dirName + "'.");
 		}
 	}
 
@@ -366,18 +380,20 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 			throw new ProjectNotExistingException("A project with the name '" + projectName + "' does not exist.");
 		}
 
+		// Create a new temporary directory
+		final File tempDir = this.createTemporaryDirectory();
 		final List<URL> libs = new ArrayList<URL>();
 
-		// Collect all libraries of the project
-
-		// Run through the libs and put them into our list.
+		// Copy and collect all libraries of the project
 		final File libDir = new File(FSProjectDAOImpl.ROOT_DIRECTORY + File.separator + projectName + File.separator + FSProjectDAOImpl.LIB_DIRECTORY);
 		final File[] files = libDir.listFiles();
 		if (files != null) {
 			for (final File file : files) {
 				if (file.getName().endsWith("." + FSProjectDAOImpl.LIB_EXTENSION)) {
+					final File newLibFile = new File(tempDir, file.getName());
+					FileCopyUtils.copy(file, newLibFile);
 					try {
-						libs.add(file.toURI().toURL());
+						libs.add(newLibFile.toURI().toURL());
 					} catch (final MalformedURLException ex) {
 						ex.printStackTrace();
 					}
@@ -393,12 +409,25 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 		final CloseableURLClassLoader classLoader = AccessController.doPrivileged(action);
 
 		// Remember the requester
-		this.classLoaders.put(classLoader, new WeakReference<Object>(requester));
+		final WeakReference<Object> ref = new WeakReference<Object>(requester);
+		this.classLoaders.put(classLoader, ref);
+		this.tempDirs.put(tempDir, ref);
 		WeakReferenceMonitor.monitor(requester, this);
 
 		return classLoader;
 	}
 
+	private File createTemporaryDirectory() {
+		int counter = 0;
+		File tempDir;
+		do {
+			tempDir = new File(FSProjectDAOImpl.TEMP_DIRECTORY, "temp" + counter);
+			counter++;
+		} while (!tempDir.mkdir());
+
+		return tempDir;
+	}
+
 	/*
 	 * (non-Javadoc)
 	 * 
@@ -614,16 +643,24 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 		synchronized (this.classLoaders) {
 			int open = 0;
 
-			final List<CloseableURLClassLoader> toBeRemoved = new ArrayList<CloseableURLClassLoader>();
+			final List<CloseableURLClassLoader> toBeClosed = new ArrayList<CloseableURLClassLoader>();
+			final List<File> toBeRemoved = new ArrayList<File>();
+
 			// Run through the class loaders and check which of them can be closed
 			for (final Entry<CloseableURLClassLoader, WeakReference<Object>> entry : this.classLoaders.entrySet()) {
 				if (entry.getValue().get() == null) {
-					toBeRemoved.add(entry.getKey());
+					toBeClosed.add(entry.getKey());
 				} else {
 					open++;
 				}
 			}
-			for (final CloseableURLClassLoader classLoader : toBeRemoved) {
+			for (final Entry<File, WeakReference<Object>> entry : this.tempDirs.entrySet()) {
+				if (entry.getValue().get() == null) {
+					toBeRemoved.add(entry.getKey());
+				}
+			}
+
+			for (final CloseableURLClassLoader classLoader : toBeClosed) {
 				try {
 					classLoader.close();
 					this.classLoaders.remove(classLoader);
@@ -632,6 +669,15 @@ public class FSProjectDAOImpl implements IProjectDAO, ReleaseListener {
 					FSProjectDAOImpl.LOG.error("Could not close classloader (" + classLoader + ")");
 				}
 			}
+			for (final File tempDir : toBeRemoved) {
+				final boolean result = FileSystemUtils.deleteRecursively(tempDir);
+				if (result) {
+					this.tempDirs.remove(tempDir);
+					FSProjectDAOImpl.LOG.info("Removed temporary directory (" + tempDir + ")");
+				} else {
+					FSProjectDAOImpl.LOG.error("Could not remove temporary directory (" + tempDir + ")");
+				}
+			}
 			FSProjectDAOImpl.LOG.info(open + " classloaders still open.");
 		}
 	}
-- 
GitLab