You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ke...@apache.org on 2005/12/12 06:13:43 UTC
svn commit: r356158 -
/geronimo/branches/1.0/modules/system/src/java/org/apache/geronimo/system/configuration/LocalConfigStore.java
Author: kevan
Date: Sun Dec 11 21:13:40 2005
New Revision: 356158
URL: http://svn.apache.org/viewcvs?rev=356158&view=rev
Log:
Insure that config-store directories that have been undeployed are eventually deleted
Modified:
geronimo/branches/1.0/modules/system/src/java/org/apache/geronimo/system/configuration/LocalConfigStore.java
Modified: geronimo/branches/1.0/modules/system/src/java/org/apache/geronimo/system/configuration/LocalConfigStore.java
URL: http://svn.apache.org/viewcvs/geronimo/branches/1.0/modules/system/src/java/org/apache/geronimo/system/configuration/LocalConfigStore.java?rev=356158&r1=356157&r2=356158&view=diff
==============================================================================
--- geronimo/branches/1.0/modules/system/src/java/org/apache/geronimo/system/configuration/LocalConfigStore.java (original)
+++ geronimo/branches/1.0/modules/system/src/java/org/apache/geronimo/system/configuration/LocalConfigStore.java Sun Dec 11 21:13:40 2005
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2003-2004 The Apache Software Foundation
+ * Copyright 2003-2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
@@ -63,11 +64,15 @@
public class LocalConfigStore implements ConfigurationStore, GBeanLifecycle {
private static final String INDEX_NAME = "index.properties";
private static final String BACKUP_NAME = "index.backup";
+ private static final String DELETE_NAME = "index.delete";
+ private final int REAPER_INTERVAL = 60 * 1000;
private final Kernel kernel;
private final ObjectName objectName;
private final URI root;
private final ServerInfo serverInfo;
private final Properties index = new Properties();
+ private final Properties pendingDeletionIndex = new Properties();
+ private ConfigStoreReaper reaper;
private final Log log;
private File rootDir;
private int maxId;
@@ -120,12 +125,32 @@
} catch (FileNotFoundException e) {
maxId = 0;
}
+
+ // See if there are old directories which we should clean up...
+ File pendingDeletionFile = new File(rootDir, DELETE_NAME);
+ try {
+ pendingDeletionIndex.load(new BufferedInputStream(new FileInputStream(pendingDeletionFile)));
+ } catch (FileNotFoundException e) {
+ // may not be one...
+ }
+
+ // Create and start the reaper...
+ reaper = new ConfigStoreReaper(REAPER_INTERVAL);
+ Thread t = new Thread(reaper, "Geronimo Config Store Reaper");
+ t.setDaemon(true);
+ t.start();
}
public void doStop() {
+ if (reaper !=null) {
+ reaper.close();
+ }
}
public void doFail() {
+ if (reaper !=null) {
+ reaper.close();
+ }
}
private void saveIndex() throws IOException {
@@ -153,6 +178,23 @@
}
}
+ private void saveDeleteIndex() throws IOException {
+ File deleteFile = new File(rootDir, DELETE_NAME);
+
+ FileOutputStream fos = new FileOutputStream(deleteFile);
+ try {
+ BufferedOutputStream os = new BufferedOutputStream(fos);
+ pendingDeletionIndex.store(os, null);
+ os.close();
+ fos = null;
+ } catch (IOException e) {
+ if (fos != null) {
+ fos.close();
+ }
+ throw e;
+ }
+ }
+
public File createNewConfigurationDir() {
// loop until we find a directory that doesn't alredy exist
// this can happen when a deployment fails (leaving an bad directory)
@@ -228,21 +270,31 @@
public void uninstall(URI configID) throws NoSuchConfigException, IOException {
String id = configID.toString();
File configDir;
+ String storeID;
synchronized(this) {
- String storeID = index.getProperty(id);
+ storeID = index.getProperty(id);
if (storeID == null) {
throw new NoSuchConfigException();
}
configDir = new File(rootDir, storeID);
- File tempDir = new File(rootDir, storeID + ".tmp");
- if (configDir.renameTo(tempDir)) {
- configDir = tempDir;
- }
index.remove(id);
saveIndex();
}
- log.debug("Uninstalled configuration " + configID);
+
delete(configDir);
+
+ // On windoze, any open file descriptor (e.g. a MultiParentClassLoader) will prevent
+ // the directory/files from being deleted. If we're unable to delete, save the directory
+ // to the pendingDeletionIndex. ConfigStoreReaper will delete when the classloader has been GC'ed.
+ if (!configDir.exists()) {
+ log.debug("Uninstalled configuration " + configID);
+ } else {
+ log.debug("Uninstalled configuration, but could not delete ConfigStore directory for " + configID);
+ synchronized (pendingDeletionIndex) {
+ pendingDeletionIndex.setProperty(configDir.toString(), id);
+ saveDeleteIndex();
+ }
+ }
}
public synchronized ObjectName loadConfiguration(URI configId) throws NoSuchConfigException, IOException, InvalidConfigException {
@@ -378,10 +430,9 @@
File file = files[i];
if (file.isDirectory()) {
delete(file);
- } else {
- if (!file.delete()) {
- file.deleteOnExit();
- };
+ }
+ else {
+ file.delete();
}
}
root.delete();
@@ -405,5 +456,74 @@
public static GBeanInfo getGBeanInfo() {
return GBEAN_INFO;
+ }
+
+ /**
+ * Thread to cleanup unused Config Store entries.
+ * On Windows, open files can't be deleted. Until MultiParentClassLoaders
+ * are GC'ed, we won't be able to delete Config Store directories/files.
+ */
+ class ConfigStoreReaper implements Runnable {
+ private final int reaperInterval;
+ private volatile boolean done = false;
+
+ public ConfigStoreReaper(int reaperInterval) {
+ this.reaperInterval = reaperInterval;
+ }
+
+ public void close() {
+ this.done = true;
+ }
+
+ public void run() {
+ log.debug("ConfigStoreReaper started");
+ while(!done) {
+ try {
+ Thread.sleep(reaperInterval);
+ } catch (InterruptedException e) {
+ continue;
+ }
+ reap();
+ }
+ }
+
+ /**
+ * For every directory in the pendingDeletionIndex, attempt to delete all
+ * sub-directories and files.
+ */
+ public void reap() {
+ // return, if there's nothing to do
+ if (pendingDeletionIndex.size() == 0)
+ return;
+ // Otherwise, attempt to delete all of the directories
+ Enumeration list = pendingDeletionIndex.propertyNames();
+ boolean dirDeleted = false;
+ while (list.hasMoreElements()) {
+ String dirName = (String)list.nextElement();
+ File deleteFile = new File(dirName);
+ try {
+ delete(deleteFile);
+ }
+ catch (IOException ioe) { // ignore errors
+ }
+ if (!deleteFile.exists()) {
+ String configName = pendingDeletionIndex.getProperty(dirName);
+ pendingDeletionIndex.remove(dirName);
+ dirDeleted = true;
+ log.debug("Reaped configuration " + configName + " in directory " + dirName);
+ }
+ }
+ // If we deleted any directories, persist the list of directories to disk...
+ if (dirDeleted) {
+ try {
+ synchronized (pendingDeletionIndex) {
+ saveDeleteIndex();
+ }
+ }
+ catch (IOException ioe) {
+ log.warn("Error saving " + DELETE_NAME + " file.", ioe);
+ }
+ }
+ }
}
}