You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ni...@apache.org on 2010/09/29 17:43:48 UTC

svn commit: r1002689 - in /commons/proper/io/trunk/src: java/org/apache/commons/io/FileSystemUtils.java java/org/apache/commons/io/ThreadMonitor.java test/org/apache/commons/io/ThreadMonitorTestCase.java

Author: niallp
Date: Wed Sep 29 15:43:48 2010
New Revision: 1002689

URL: http://svn.apache.org/viewvc?rev=1002689&view=rev
Log:
IO-185 FileSystemUtils add freeSpaceKb() methods that take a timeout parameter - fixes FileSystemUtils.freeSpaceWindows blocks - reported by Martin Thelian

Added:
    commons/proper/io/trunk/src/java/org/apache/commons/io/ThreadMonitor.java   (with props)
    commons/proper/io/trunk/src/test/org/apache/commons/io/ThreadMonitorTestCase.java   (with props)
Modified:
    commons/proper/io/trunk/src/java/org/apache/commons/io/FileSystemUtils.java

Modified: commons/proper/io/trunk/src/java/org/apache/commons/io/FileSystemUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/FileSystemUtils.java?rev=1002689&r1=1002688&r2=1002689&view=diff
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/FileSystemUtils.java (original)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/FileSystemUtils.java Wed Sep 29 15:43:48 2010
@@ -145,7 +145,7 @@ public class FileSystemUtils {
      */
     @Deprecated
     public static long freeSpace(String path) throws IOException {
-        return INSTANCE.freeSpaceOS(path, OS, false);
+        return INSTANCE.freeSpaceOS(path, OS, false, -1);
     }
 
     //-----------------------------------------------------------------------
@@ -174,7 +174,36 @@ public class FileSystemUtils {
      * @since Commons IO 1.2, enhanced OS support in 1.3
      */
     public static long freeSpaceKb(String path) throws IOException {
-        return INSTANCE.freeSpaceOS(path, OS, true);
+        return freeSpaceKb(path, -1);
+    }
+    /**
+     * Returns the free space on a drive or volume in kilobytes by invoking
+     * the command line.
+     * <pre>
+     * FileSystemUtils.freeSpaceKb("C:");       // Windows
+     * FileSystemUtils.freeSpaceKb("/volume");  // *nix
+     * </pre>
+     * The free space is calculated via the command line.
+     * It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
+     * <p>
+     * In order to work, you must be running Windows, or have a implementation of
+     * Unix df that supports GNU format when passed -k (or -kP). If you are going
+     * to rely on this code, please check that it works on your OS by running
+     * some simple tests to compare the command line with the output from this class.
+     * If your operating system isn't supported, please raise a JIRA call detailing
+     * the exact result from df -k and as much other detail as possible, thanks.
+     *
+     * @param path  the path to get free space for, not null, not empty on Unix
+     * @param timeout The timout amount in milliseconds or no timeout if the value
+     *  is zero or less
+     * @return the amount of free drive space on the drive or volume in kilobytes
+     * @throws IllegalArgumentException if the path is invalid
+     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IOException if an error occurs when finding the free space
+     * @since Commons IO 2.0
+     */
+    public static long freeSpaceKb(String path, long timeout) throws IOException {
+        return INSTANCE.freeSpaceOS(path, OS, true, timeout);
     }
 
     /**
@@ -189,7 +218,25 @@ public class FileSystemUtils {
      * @throws IOException if an error occurs when finding the free space
      */
     public static long freeSpaceKb() throws IOException {
-        return freeSpaceKb(new File(".").getAbsolutePath()); 
+        return freeSpaceKb(-1); 
+    }
+
+    /**
+     * Returns the disk size of the volume which holds the working directory.
+     * <p>
+     * Identical to:
+     * <pre>
+     * freeSpaceKb(new File(".").getAbsolutePath())
+     * </pre>
+     * @param timeout The timout amount in milliseconds or no timeout if the value
+     *  is zero or less
+     * @return the amount of free drive space on the drive or volume in kilobytes
+     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IOException if an error occurs when finding the free space
+     * @since Commons IO 2.0
+     */
+    public static long freeSpaceKb(long timeout) throws IOException {
+        return freeSpaceKb(new File(".").getAbsolutePath(), timeout); 
     }
     
     //-----------------------------------------------------------------------
@@ -206,22 +253,24 @@ public class FileSystemUtils {
      * @param path  the path to get free space for, not null, not empty on Unix
      * @param os  the operating system code
      * @param kb  whether to normalize to kilobytes
+     * @param timeout The timout amount in milliseconds or no timeout if the value
+     *  is zero or less
      * @return the amount of free drive space on the drive or volume
      * @throws IllegalArgumentException if the path is invalid
      * @throws IllegalStateException if an error occurred in initialisation
      * @throws IOException if an error occurs when finding the free space
      */
-    long freeSpaceOS(String path, int os, boolean kb) throws IOException {
+    long freeSpaceOS(String path, int os, boolean kb, long timeout) throws IOException {
         if (path == null) {
             throw new IllegalArgumentException("Path must not be empty");
         }
         switch (os) {
             case WINDOWS:
-                return (kb ? freeSpaceWindows(path) / 1024 : freeSpaceWindows(path));
+                return (kb ? freeSpaceWindows(path, timeout) / 1024 : freeSpaceWindows(path, timeout));
             case UNIX:
-                return freeSpaceUnix(path, kb, false);
+                return freeSpaceUnix(path, kb, false, timeout);
             case POSIX_UNIX:
-                return freeSpaceUnix(path, kb, true);
+                return freeSpaceUnix(path, kb, true, timeout);
             case OTHER:
                 throw new IllegalStateException("Unsupported operating system");
             default:
@@ -235,10 +284,12 @@ public class FileSystemUtils {
      * Find free space on the Windows platform using the 'dir' command.
      *
      * @param path  the path to get free space for, including the colon
+     * @param timeout The timout amount in milliseconds or no timeout if the value
+     *  is zero or less
      * @return the amount of free drive space on the drive
      * @throws IOException if an error occurs
      */
-    long freeSpaceWindows(String path) throws IOException {
+    long freeSpaceWindows(String path, long timeout) throws IOException {
         path = FilenameUtils.normalize(path);
         if (path.length() > 2 && path.charAt(1) == ':') {
             path = path.substring(0, 2);  // seems to make it work
@@ -248,7 +299,7 @@ public class FileSystemUtils {
         String[] cmdAttribs = new String[] {"cmd.exe", "/C", "dir /-c " + path};
         
         // read in the output of the command to an ArrayList
-        List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE);
+        List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE, timeout);
         
         // now iterate over the lines we just read and find the LAST
         // non-empty line (the free space bytes should be in the last element
@@ -325,10 +376,12 @@ public class FileSystemUtils {
      * @param path  the path to get free space for
      * @param kb  whether to normalize to kilobytes
      * @param posix  whether to use the posix standard format flag
+     * @param timeout The timout amount in milliseconds or no timeout if the value
+     *  is zero or less
      * @return the amount of free drive space on the volume
      * @throws IOException if an error occurs
      */
-    long freeSpaceUnix(String path, boolean kb, boolean posix) throws IOException {
+    long freeSpaceUnix(String path, boolean kb, boolean posix, long timeout) throws IOException {
         if (path.length() == 0) {
             throw new IllegalArgumentException("Path must not be empty");
         }
@@ -345,7 +398,7 @@ public class FileSystemUtils {
             (flags.length() > 1 ? new String[] {DF, flags, path} : new String[] {DF, path});
         
         // perform the command, asking for up to 3 lines (header, interesting, overflow)
-        List<String> lines = performCommand(cmdAttribs, 3);
+        List<String> lines = performCommand(cmdAttribs, 3, timeout);
         if (lines.size() < 2) {
             // unknown problem, throw exception
             throw new IOException(
@@ -407,10 +460,12 @@ public class FileSystemUtils {
      *
      * @param cmdAttribs  the command line parameters
      * @param max The maximum limit for the lines returned
+     * @param timeout The timout amount in milliseconds or no timeout if the value
+     *  is zero or less
      * @return the parsed data
      * @throws IOException if an error occurs
      */
-    List<String> performCommand(String[] cmdAttribs, int max) throws IOException {
+    List<String> performCommand(String[] cmdAttribs, int max, long timeout) throws IOException {
         // this method does what it can to avoid the 'Too many open files' error
         // based on trial and error and these links:
         // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
@@ -426,6 +481,9 @@ public class FileSystemUtils {
         InputStream err = null;
         BufferedReader inr = null;
         try {
+
+            Thread monitor = ThreadMonitor.start(timeout);
+
             proc = openProcess(cmdAttribs);
             in = proc.getInputStream();
             out = proc.getOutputStream();
@@ -439,6 +497,9 @@ public class FileSystemUtils {
             }
             
             proc.waitFor();
+
+            ThreadMonitor.stop(monitor);
+
             if (proc.exitValue() != 0) {
                 // os command problem, throw exception
                 throw new IOException(

Added: commons/proper/io/trunk/src/java/org/apache/commons/io/ThreadMonitor.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/java/org/apache/commons/io/ThreadMonitor.java?rev=1002689&view=auto
==============================================================================
--- commons/proper/io/trunk/src/java/org/apache/commons/io/ThreadMonitor.java (added)
+++ commons/proper/io/trunk/src/java/org/apache/commons/io/ThreadMonitor.java Wed Sep 29 15:43:48 2010
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io;
+
+/**
+ * Monitors a thread, interrupting it of it reaches the specified timout.
+ * <p>
+ * This works by sleeping until the specified timout amount and then
+ * interrupting the thread being monitored. If the thread being monitored
+ * completes its work before being interrupted, it should <code>interrupt()<code>
+ * the <i>monitor</i> thread.
+ * <p>
+ * 
+ * <pre>
+ *       long timeoutInMillis = 1000;
+ *       try {
+ *           Thread monitor = ThreadMonitor.start(timeoutInMillis);
+ *           // do some work here
+ *           ThreadMonitor.stop(monitor);
+ *       } catch (InterruptedException e) {
+ *           // timed amount was reached
+ *       }
+ * </pre>
+ *
+ * @version  $Id$
+ */
+class ThreadMonitor implements Runnable {
+
+    private final Thread thread;
+    private final long timeout;
+
+    /**
+     * Start monitoring the current thread.
+     *
+     * @param timeout The timout amount in milliseconds
+     * or no timeout if the value is zero or less
+     * @return The monitor thread or <code>null</code>
+     * if the timout amount is not greater than zero
+     */
+    public static Thread start(long timeout) {
+        return start(Thread.currentThread(), timeout);
+    }
+
+    /**
+     * Start monitoring the specified thread.
+     *
+     * @param thread The thread The thread to monitor
+     * @param timeout The timout amount in milliseconds
+     * or no timeout if the value is zero or less
+     * @return The monitor thread or <code>null</code>
+     * if the timout amount is not greater than zero
+     */
+    public static Thread start(Thread thread, long timeout) {
+        Thread monitor = null;
+        if (timeout > 0) {
+            ThreadMonitor timout = new ThreadMonitor(thread, timeout);
+            monitor = new Thread(timout, ThreadMonitor.class.getSimpleName());
+            monitor.setDaemon(true);
+            monitor.start();
+        }
+        return monitor;
+    }
+
+    /**
+     * Stop monitoring the specified thread.
+     *
+     * @param thread The monitor thread, may be <code>null</code>
+     */
+    public static void stop(Thread thread) {
+        if (thread != null) {
+            thread.interrupt();
+        }
+    }
+
+    /**
+     * Construct and new monitor.
+     *
+     * @param thread The thread to monitor
+     * @param timeout The timout amount in milliseconds
+     */
+    private ThreadMonitor(Thread thread, long timeout) {
+        this.thread = thread;
+        this.timeout = timeout;
+    }
+
+    /**
+     * Sleep until the specified timout amount and then
+     * interrupt the thread being monitored.
+     *
+     * @see Runnable#run()
+     */
+    public void run() {
+        try {
+            Thread.sleep(timeout);
+            thread.interrupt();
+        } catch (InterruptedException e) {
+            // timeout not reached
+        }
+    }
+}
\ No newline at end of file

Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/ThreadMonitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/io/trunk/src/java/org/apache/commons/io/ThreadMonitor.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Added: commons/proper/io/trunk/src/test/org/apache/commons/io/ThreadMonitorTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/io/trunk/src/test/org/apache/commons/io/ThreadMonitorTestCase.java?rev=1002689&view=auto
==============================================================================
--- commons/proper/io/trunk/src/test/org/apache/commons/io/ThreadMonitorTestCase.java (added)
+++ commons/proper/io/trunk/src/test/org/apache/commons/io/ThreadMonitorTestCase.java Wed Sep 29 15:43:48 2010
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link ThreadMonitor}.
+ */
+public class ThreadMonitorTestCase extends TestCase {
+
+
+    public ThreadMonitorTestCase(String name) {
+        super(name);
+    }
+
+    /**
+     * Test timeout.
+     */
+    public void testTimeout() {
+        try {
+            Thread monitor = ThreadMonitor.start(100);
+            Thread.sleep(200);
+            ThreadMonitor.stop(monitor);
+            fail("Expected InterruptedException");
+        } catch (InterruptedException e) {
+            // expected result - timout
+        }
+    }
+
+    /**
+     * Test task completed before timeout.
+     */
+    public void testCompletedWithoutTimeout() {
+        try {
+            Thread monitor = ThreadMonitor.start(200);
+            Thread.sleep(100);
+            ThreadMonitor.stop(monitor);
+        } catch (InterruptedException e) {
+            fail("Timed Out");
+        }
+    }
+
+    /**
+     * Test No timeout.
+     */
+    public void testNoTimeout() {
+
+        // timeout = -1
+        try {
+            Thread monitor = ThreadMonitor.start(-1);
+            assertNull("Timeout -1, Monitor should be null", monitor);
+            Thread.sleep(100);
+            ThreadMonitor.stop(monitor);
+        } catch (Exception e) {
+            fail("Timeout -1, threw " + e);
+        }
+
+        // timeout = 0
+        try {
+            Thread monitor = ThreadMonitor.start(0);
+            assertNull("Timeout 0, Monitor should be null", monitor);
+            Thread.sleep(100);
+            ThreadMonitor.stop(monitor);
+        } catch (Exception e) {
+            fail("Timeout 0, threw " + e);
+        }
+    }
+}
+

Propchange: commons/proper/io/trunk/src/test/org/apache/commons/io/ThreadMonitorTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/io/trunk/src/test/org/apache/commons/io/ThreadMonitorTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL