Reduce thread leaks in tests from 76 to 29.
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/VFS.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/VFS.java
index c8614f0..43250aa 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/VFS.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/VFS.java
@@ -53,17 +53,18 @@
private static FileSystemManager createFileSystemManager(final String managerClassName) throws FileSystemException {
try {
// Create instance
- final Class<?> mgrClass = Class.forName(managerClassName);
- final FileSystemManager mgr = (FileSystemManager) mgrClass.newInstance();
+ final Class<FileSystemManager> clazz = (Class<FileSystemManager>) Class.forName(managerClassName);
+ final FileSystemManager manager = clazz.newInstance();
try {
// Initialize
- mgrClass.getMethod("init", (Class[]) null).invoke(mgr, (Object[]) null);
- } catch (final NoSuchMethodException ignored) {
+ clazz.getMethod("init", (Class[]) null).invoke(manager, (Object[]) null);
+ } catch (final NoSuchMethodException e) {
/* Ignore; don't initialize. */
+ e.printStackTrace();
}
- return mgr;
+ return manager;
} catch (final InvocationTargetException e) {
throw new FileSystemException("vfs/create-manager.error", managerClassName, e.getTargetException());
} catch (final Exception e) {
@@ -101,7 +102,7 @@
* @throws FileSystemException if an error occurs creating the manager.
* @since 2.5.0
*/
- public static FileSystemManager reset() throws FileSystemException {
+ public static synchronized FileSystemManager reset() throws FileSystemException {
close();
return instance = createFileSystemManager("org.apache.commons.vfs2.impl.StandardFileSystemManager");
}
@@ -124,5 +125,6 @@
}
private VFS() {
+ // no public instantiation.
}
}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/cache/SoftRefFilesCache.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/cache/SoftRefFilesCache.java
index 4e2da10..1b283ba 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/cache/SoftRefFilesCache.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/cache/SoftRefFilesCache.java
@@ -30,28 +30,20 @@
import org.apache.commons.vfs2.FileSystem;
/**
- * This implementation caches every file as long as it is strongly reachable by the java vm. As soon as the vm needs
+ * This implementation caches every file as long as it is strongly reachable by the JVM. As soon as the JVM needs
* memory - every softly reachable file will be discarded.
*
* @see SoftReference
*/
public class SoftRefFilesCache extends AbstractFilesCache {
- private static final Log log = LogFactory.getLog(SoftRefFilesCache.class);
-
- private final Map<FileSystem, Map<FileName, Reference<FileObject>>> fileSystemCache = new HashMap<>();
- private final Map<Reference<FileObject>, FileSystemAndNameKey> refReverseMap = new HashMap<>(100);
- private final ReferenceQueue<FileObject> refQueue = new ReferenceQueue<>();
-
- private SoftRefReleaseThread softRefReleaseThread;
-
/**
- * This thread will listen on the ReferenceQueue and remove the entry in the filescache as soon as the vm removes
- * the reference
+ * This thread will listen on the ReferenceQueue and remove the entry in the file cache as soon as the JVM removes
+ * the reference.
*/
- private final class SoftRefReleaseThread extends Thread {
- private SoftRefReleaseThread() {
- setName(SoftRefReleaseThread.class.getName());
+ private final class ReleaseThread extends Thread {
+ private ReleaseThread() {
+ setName(ReleaseThread.class.getName());
setDaemon(true);
}
@@ -59,37 +51,114 @@
public void run() {
try {
while (true) {
- final Reference<?> ref = refQueue.remove(0);
- if (ref == null) {
- continue;
- }
-
- removeFile(ref);
+ removeFile(refQueue.remove(0));
}
} catch (final InterruptedException e) {
+ // end thread run.
+ // System.out.println("Thread caught InterruptedException, ending " + getId());
+ // System.out.flush();
}
}
}
+ private static final Log log = LogFactory.getLog(SoftRefFilesCache.class);
+ private final Map<FileSystem, Map<FileName, Reference<FileObject>>> fileSystemCache = new HashMap<>();
+ private final Map<Reference<FileObject>, FileSystemAndNameKey> refReverseMap = new HashMap<>(100);
+ private final ReferenceQueue<FileObject> refQueue = new ReferenceQueue<>();
+ private ReleaseThread releaseThread;
+
+ /**
+ * Constructs a new instance.
+ */
public SoftRefFilesCache() {
+ // empty
}
- private synchronized void startThread() {
- if (softRefReleaseThread == null) {
- softRefReleaseThread = new SoftRefReleaseThread();
- softRefReleaseThread.start();
+ @Override
+ public synchronized void clear(final FileSystem fileSystem) {
+ final Map<FileName, Reference<FileObject>> files = getOrCreateFilesystemCache(fileSystem);
+ final Iterator<FileSystemAndNameKey> iterKeys = refReverseMap.values().iterator();
+
+ while (iterKeys.hasNext()) {
+ final FileSystemAndNameKey key = iterKeys.next();
+ if (key.getFileSystem() == fileSystem) {
+ iterKeys.remove();
+ files.remove(key.getFileName());
+ }
}
+
+ if (files.isEmpty()) {
+ close(fileSystem);
+ }
+ }
+
+ @Override
+ public synchronized void close() {
+ super.close();
+ endThread();
+ fileSystemCache.clear();
+ refReverseMap.clear();
+ }
+
+ /**
+ * @param fileSystem The file system to close.
+ */
+ private synchronized void close(final FileSystem fileSystem) {
+ if (log.isDebugEnabled()) {
+ log.debug("Close FileSystem: " + fileSystem.getRootName());
+ }
+
+ fileSystemCache.remove(fileSystem);
+ if (fileSystemCache.isEmpty()) {
+ endThread();
+ }
+ }
+
+ protected Reference<FileObject> createReference(final FileObject file, final ReferenceQueue<FileObject> refqueue) {
+ return new SoftReference<>(file, refqueue);
}
private synchronized void endThread() {
- final SoftRefReleaseThread thread = softRefReleaseThread;
- softRefReleaseThread = null;
+ final ReleaseThread thread = releaseThread;
+ releaseThread = null;
if (thread != null) {
thread.interrupt();
}
}
@Override
+ public synchronized FileObject getFile(final FileSystem fileSystem, final FileName fileName) {
+ final Map<FileName, Reference<FileObject>> files = getOrCreateFilesystemCache(fileSystem);
+
+ final Reference<FileObject> ref = files.get(fileName);
+ if (ref == null) {
+ return null;
+ }
+
+ final FileObject fo = ref.get();
+ if (fo == null) {
+ removeFile(fileSystem, fileName);
+ }
+ return fo;
+ }
+
+ protected synchronized Map<FileName, Reference<FileObject>> getOrCreateFilesystemCache(final FileSystem fileSystem) {
+ if (fileSystemCache.isEmpty()) {
+ startThread();
+ }
+
+ return fileSystemCache.computeIfAbsent(fileSystem, k -> new HashMap<>());
+ }
+
+ private String getSafeName(final FileName fileName) {
+ return fileName.getFriendlyURI();
+ }
+
+ private String getSafeName(final FileObject fileObject) {
+ return this.getSafeName(fileObject.getName());
+ }
+
+ @Override
public void putFile(final FileObject fileObject) {
if (log.isDebugEnabled()) {
log.debug("putFile: " + this.getSafeName(fileObject));
@@ -109,14 +178,6 @@
}
}
- private String getSafeName(final FileName fileName) {
- return fileName.getFriendlyURI();
- }
-
- private String getSafeName(final FileObject fileObject) {
- return this.getSafeName(fileObject.getName());
- }
-
@Override
public boolean putFileIfAbsent(final FileObject fileObject) {
if (log.isDebugEnabled()) {
@@ -141,67 +202,6 @@
}
}
- protected Reference<FileObject> createReference(final FileObject file, final ReferenceQueue<FileObject> refqueue) {
- return new SoftReference<>(file, refqueue);
- }
-
- @Override
- public synchronized FileObject getFile(final FileSystem fileSystem, final FileName fileName) {
- final Map<FileName, Reference<FileObject>> files = getOrCreateFilesystemCache(fileSystem);
-
- final Reference<FileObject> ref = files.get(fileName);
- if (ref == null) {
- return null;
- }
-
- final FileObject fo = ref.get();
- if (fo == null) {
- removeFile(fileSystem, fileName);
- }
- return fo;
- }
-
- @Override
- public synchronized void clear(final FileSystem fileSystem) {
- final Map<FileName, Reference<FileObject>> files = getOrCreateFilesystemCache(fileSystem);
-
- final Iterator<FileSystemAndNameKey> iterKeys = refReverseMap.values().iterator();
- while (iterKeys.hasNext()) {
- final FileSystemAndNameKey key = iterKeys.next();
- if (key.getFileSystem() == fileSystem) {
- iterKeys.remove();
- files.remove(key.getFileName());
- }
- }
-
- if (files.isEmpty()) {
- close(fileSystem);
- }
- }
-
- /**
- * @param fileSystem The file system to close.
- */
- private synchronized void close(final FileSystem fileSystem) {
- if (log.isDebugEnabled()) {
- log.debug("close fs: " + fileSystem.getRootName());
- }
-
- fileSystemCache.remove(fileSystem);
- if (fileSystemCache.isEmpty()) {
- endThread();
- }
- }
-
- @Override
- public synchronized void close() {
- endThread();
-
- fileSystemCache.clear();
-
- refReverseMap.clear();
- }
-
@Override
public synchronized void removeFile(final FileSystem fileSystem, final FileName fileName) {
if (removeFile(new FileSystemAndNameKey(fileSystem, fileName))) {
@@ -226,17 +226,25 @@
private synchronized void removeFile(final Reference<?> ref) {
final FileSystemAndNameKey key = refReverseMap.get(ref);
-
if (key != null && removeFile(key)) {
close(key.getFileSystem());
}
}
- protected synchronized Map<FileName, Reference<FileObject>> getOrCreateFilesystemCache(final FileSystem fileSystem) {
- if (fileSystemCache.isEmpty()) {
- startThread();
+ private synchronized void startThread() {
+ if (releaseThread == null) {
+ releaseThread = new ReleaseThread();
+ releaseThread.start();
+ // System.out.println("Started thread ID " + releaseThread.getId());
+ // System.out.flush();
+ // Thread.dumpStack();
}
+ }
- return fileSystemCache.computeIfAbsent(fileSystem, k -> new HashMap<>());
+ @Override
+ public String toString() {
+ return super.toString() + " [releaseThread=" + releaseThread
+ + (releaseThread == null ? "" : "(ID " + releaseThread.getId() + " is " + releaseThread.getState() + ")")
+ + "]";
}
}
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/impl/DefaultFileSystemManager.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/impl/DefaultFileSystemManager.java
index 0e5ca5d..b47c256 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/impl/DefaultFileSystemManager.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/impl/DefaultFileSystemManager.java
@@ -290,7 +290,6 @@
if (init) {
throw new FileSystemException("vfs.impl/already-inited.error");
}
-
this.filesCache = filesCache;
}
@@ -459,8 +458,7 @@
private void closeComponent(final Object component) {
if (component != null && components.contains(component)) {
if (component instanceof VfsComponent) {
- final VfsComponent vfsComponent = (VfsComponent) component;
- vfsComponent.close();
+ ((VfsComponent) component).close();
}
components.remove(component);
}
@@ -504,7 +502,6 @@
}
if (filesCache == null) {
- // filesCache = new DefaultFilesCache();
filesCache = new SoftRefFilesCache();
}
if (fileCacheStrategy == null) {
@@ -543,16 +540,14 @@
closeComponent(vfsProvider);
closeComponent(fileReplicator);
closeComponent(tempFileStore);
- closeComponent(filesCache);
closeComponent(defaultProvider);
-
// unregister all providers here, so if any components have local file references
// they can still resolve against the supported schemes
providers.clear();
// FileOperations are components, too
- operationProviders.values().forEach(opproviders -> opproviders.forEach(this::closeComponent));
+ operationProviders.values().forEach(opProviders -> opProviders.forEach(this::closeComponent));
// unregister all
operationProviders.clear();
@@ -572,6 +567,11 @@
// virtual schemas
virtualFileSystemSchemes.clear();
+ // Close cache last.
+ if (filesCache != null) {
+ filesCache.close();
+ }
+
// setters and derived state
defaultProvider = null;
baseFile = null;
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/AbstractProviderTestConfig.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/AbstractProviderTestConfig.java
index 0973ec7..2e4e193 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/AbstractProviderTestConfig.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/AbstractProviderTestConfig.java
@@ -24,7 +24,16 @@
*/
public abstract class AbstractProviderTestConfig extends AbstractProviderTestCase implements ProviderTestConfig {
- private FilesCache cache;
+ private FilesCache filesCache;
+
+ /**
+ * Subclasses can override.
+ *
+ * @return A new cache.
+ */
+ protected FilesCache createFilesCache() {
+ return new SoftRefFilesCache();
+ }
/**
* Returns a DefaultFileSystemManager instance (or subclass instance).
@@ -35,13 +44,11 @@
}
@Override
- public FilesCache getFilesCache() {
- if (cache == null) {
- // cache = new DefaultFilesCache();
- cache = new SoftRefFilesCache();
+ public final FilesCache getFilesCache() {
+ if (filesCache == null) {
+ filesCache = createFilesCache();
}
-
- return cache;
+ return filesCache;
}
@Override
@@ -57,4 +64,12 @@
// default is do nothing.
}
+ @Override
+ public void tearDown() throws Exception {
+ if (filesCache != null) {
+ filesCache.close();
+ // Give a chance for any threads to end.
+ Thread.sleep(20);
+ }
+ }
}
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/AbstractTestSuite.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/AbstractTestSuite.java
index 0ccaae8..c0ee2bd 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/AbstractTestSuite.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/AbstractTestSuite.java
@@ -211,6 +211,8 @@
manager.freeUnusedResources();
manager.close();
+ // Give a chance for any threads to end.
+ Thread.sleep(20);
// Make sure temp directory is empty or gone
checkTempDir("Temp dir not empty after test");
@@ -226,7 +228,7 @@
* if (providerConfig.checkCleanThreadState()) { // close the manager to do a "not thread safe" release of
* all resources // and allow the vm to shutdown manager.close(); fail(message); } else {
*/
- System.out.println(message);
+ System.out.print(message);
// }
}
// System.in.read();
@@ -238,7 +240,7 @@
private void checkTempDir(final String assertMsg) {
if (tempDir.exists()) {
Assert.assertTrue(assertMsg + " (" + tempDir.getAbsolutePath() + ")",
- tempDir.isDirectory() && tempDir.list().length == 0);
+ tempDir.isDirectory() && ArrayUtils.isEmpty(tempDir.list()));
}
}
@@ -247,7 +249,7 @@
return StringUtils.EMPTY;
}
final StringBuffer sb = new StringBuffer(256);
- sb.append("Created threads still running:");
+ sb.append("Threads still running (" + threadSnapshot.length + "): ");
sb.append(System.lineSeparator());
Field threadTargetField = null;
@@ -261,53 +263,49 @@
int liveCount = 0;
for (int index = 0; index < threadSnapshot.length; index++) {
final Thread thread = threadSnapshot[index];
- if (thread == null || !thread.isAlive()) {
- continue;
- }
- liveCount++;
- sb.append("Thread[");
- sb.append(index);
- sb.append("]: ");
- sb.append(" ID ");
- sb.append(thread.getId());
- sb.append(", ");
- // prints [name,priority,group]
- sb.append(thread);
- sb.append(", ");
- sb.append(thread.getState());
- sb.append(", ");
- if (!thread.isDaemon()) {
- sb.append("non_");
- }
- sb.append("daemon");
-
- if (threadTargetField != null) {
+ if (thread != null && thread.isAlive()) {
+ liveCount++;
+ sb.append("\tThread[");
+ sb.append(index);
+ sb.append("] ");
+ sb.append(" ID ");
+ sb.append(thread.getId());
sb.append(", ");
- try {
- final Object threadTarget = threadTargetField.get(thread);
- if (threadTarget != null) {
- sb.append(threadTarget.getClass());
- } else {
- sb.append("null");
- }
- } catch (final IllegalAccessException e) {
- sb.append("unknown (");
- sb.append(e);
- sb.append(")");
+ // prints [name,priority,group]
+ sb.append(thread);
+ sb.append(",\t");
+ sb.append(thread.getState());
+ sb.append(", ");
+ if (!thread.isDaemon()) {
+ sb.append("non_");
}
- }
+ sb.append("daemon");
- sb.append(System.lineSeparator());
-// Stream.of(thread.getStackTrace()).forEach(e -> {
-// sb.append('\t');
-// sb.append(e);
-// sb.append(System.lineSeparator());
-// });
+ if (threadTargetField != null) {
+ sb.append("\t, ");
+ try {
+ final Object threadTarget = threadTargetField.get(thread);
+ if (threadTarget != null) {
+ sb.append(threadTarget.getClass());
+ } else {
+ sb.append("null");
+ }
+ } catch (final IllegalAccessException e) {
+ sb.append("unknown (");
+ sb.append(e);
+ sb.append(")");
+ }
+ }
+
+ sb.append(System.lineSeparator());
+// Stream.of(thread.getStackTrace()).forEach(e -> {
+// sb.append('\t');
+// sb.append(e);
+// sb.append(System.lineSeparator());
+// });
+ }
}
- if (liveCount == 0) {
- return StringUtils.EMPTY;
- }
- return sb.toString();
+ return liveCount == 0 ? StringUtils.EMPTY : sb.toString();
}
private Thread[] diffThreadSnapshot(final Thread[] startThreadSnapshot, final Thread[] endThreadSnapshot) {
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/ProviderTestSuite.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/ProviderTestSuite.java
index 837d923..a142197 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/ProviderTestSuite.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/ProviderTestSuite.java
@@ -63,4 +63,5 @@
addTests(UrlStructureTests.class);
addTests(VfsClassLoaderTests.class);
}
+
}
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/DefaultFilesCacheTestCase.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/DefaultFilesCacheTestCase.java
index b963a72..2462541 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/DefaultFilesCacheTestCase.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/DefaultFilesCacheTestCase.java
@@ -38,7 +38,7 @@
}
@Override
- public FilesCache getFilesCache() {
+ public FilesCache createFilesCache() {
return new DefaultFilesCache();
}
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/LRUFilesCacheTestCase.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/LRUFilesCacheTestCase.java
index 5a37f06..2156ed7 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/LRUFilesCacheTestCase.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/LRUFilesCacheTestCase.java
@@ -38,7 +38,7 @@
}
@Override
- public FilesCache getFilesCache() {
+ public FilesCache createFilesCache() {
return new LRUFilesCache(5);
}
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/NullFilesCacheTestCase.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/NullFilesCacheTestCase.java
index a1b60c0..34912e0 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/NullFilesCacheTestCase.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/NullFilesCacheTestCase.java
@@ -38,7 +38,7 @@
}
@Override
- public FilesCache getFilesCache() {
+ public FilesCache createFilesCache() {
return new NullFilesCache();
}
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/SoftRefFilesCacheTestCase.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/SoftRefFilesCacheTestCase.java
index 804c7ad..50c06d0 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/SoftRefFilesCacheTestCase.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/SoftRefFilesCacheTestCase.java
@@ -36,7 +36,7 @@
}
@Override
- public FilesCache getFilesCache() {
+ public FilesCache createFilesCache() {
return new SoftRefFilesCache();
}
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/SoftRefFilesCacheTests.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/SoftRefFilesCacheTests.java
index cb48e44..817dea5 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/SoftRefFilesCacheTests.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/SoftRefFilesCacheTests.java
@@ -36,7 +36,7 @@
}
@Test
- public void testClass() {
+ public void testFilesCacheClass() {
assertTrue(getManager().getFilesCache() instanceof SoftRefFilesCache);
}
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/WeakRefFilesCacheTestCase.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/WeakRefFilesCacheTestCase.java
index 45aed54..7a2bad1 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/WeakRefFilesCacheTestCase.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/cache/WeakRefFilesCacheTestCase.java
@@ -38,7 +38,7 @@
}
@Override
- public FilesCache getFilesCache() {
+ public FilesCache createFilesCache() {
return new WeakRefFilesCache();
}
diff --git a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/url/UrlProviderHttpTestCase.java b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/url/UrlProviderHttpTestCase.java
index 03a08d5..842c333 100644
--- a/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/url/UrlProviderHttpTestCase.java
+++ b/commons-vfs2/src/test/java/org/apache/commons/vfs2/provider/url/UrlProviderHttpTestCase.java
@@ -20,10 +20,12 @@
import java.io.IOException;
import java.util.concurrent.TimeUnit;
+import org.apache.commons.AbstractVfsTestCase;
import org.apache.commons.vfs2.AbstractProviderTestConfig;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.ProviderTestSuite;
+import org.apache.commons.vfs2.VFS;
import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
import org.apache.commons.vfs2.util.NHttpFileServer;
@@ -80,10 +82,8 @@
/**
* Stops the embedded Apache HTTP Server ().
- *
- * @throws IOException
*/
- private static void tearDownClass() throws IOException {
+ public static void tearDownClass() {
if (Server != null) {
Server.shutdown(5000, TimeUnit.SECONDS);
}