Skip to content

Commit ab42367

Browse files
author
Niall Pemberton
committed
IO-185 FileSystemUtils add freeSpaceKb() methods that take a timeout parameter - fixes FileSystemUtils.freeSpaceWindows blocks - reported by Martin Thelian
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/io/trunk@1002689 13f79535-47bb-0310-9956-ffa450edef68
1 parent d062bd0 commit ab42367

3 files changed

Lines changed: 271 additions & 12 deletions

File tree

src/java/org/apache/commons/io/FileSystemUtils.java

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public FileSystemUtils() {
145145
*/
146146
@Deprecated
147147
public static long freeSpace(String path) throws IOException {
148-
return INSTANCE.freeSpaceOS(path, OS, false);
148+
return INSTANCE.freeSpaceOS(path, OS, false, -1);
149149
}
150150

151151
//-----------------------------------------------------------------------
@@ -174,7 +174,36 @@ public static long freeSpace(String path) throws IOException {
174174
* @since Commons IO 1.2, enhanced OS support in 1.3
175175
*/
176176
public static long freeSpaceKb(String path) throws IOException {
177-
return INSTANCE.freeSpaceOS(path, OS, true);
177+
return freeSpaceKb(path, -1);
178+
}
179+
/**
180+
* Returns the free space on a drive or volume in kilobytes by invoking
181+
* the command line.
182+
* <pre>
183+
* FileSystemUtils.freeSpaceKb("C:"); // Windows
184+
* FileSystemUtils.freeSpaceKb("/volume"); // *nix
185+
* </pre>
186+
* The free space is calculated via the command line.
187+
* It uses 'dir /-c' on Windows, 'df -kP' on AIX/HP-UX and 'df -k' on other Unix.
188+
* <p>
189+
* In order to work, you must be running Windows, or have a implementation of
190+
* Unix df that supports GNU format when passed -k (or -kP). If you are going
191+
* to rely on this code, please check that it works on your OS by running
192+
* some simple tests to compare the command line with the output from this class.
193+
* If your operating system isn't supported, please raise a JIRA call detailing
194+
* the exact result from df -k and as much other detail as possible, thanks.
195+
*
196+
* @param path the path to get free space for, not null, not empty on Unix
197+
* @param timeout The timout amount in milliseconds or no timeout if the value
198+
* is zero or less
199+
* @return the amount of free drive space on the drive or volume in kilobytes
200+
* @throws IllegalArgumentException if the path is invalid
201+
* @throws IllegalStateException if an error occurred in initialisation
202+
* @throws IOException if an error occurs when finding the free space
203+
* @since Commons IO 2.0
204+
*/
205+
public static long freeSpaceKb(String path, long timeout) throws IOException {
206+
return INSTANCE.freeSpaceOS(path, OS, true, timeout);
178207
}
179208

180209
/**
@@ -189,7 +218,25 @@ public static long freeSpaceKb(String path) throws IOException {
189218
* @throws IOException if an error occurs when finding the free space
190219
*/
191220
public static long freeSpaceKb() throws IOException {
192-
return freeSpaceKb(new File(".").getAbsolutePath());
221+
return freeSpaceKb(-1);
222+
}
223+
224+
/**
225+
* Returns the disk size of the volume which holds the working directory.
226+
* <p>
227+
* Identical to:
228+
* <pre>
229+
* freeSpaceKb(new File(".").getAbsolutePath())
230+
* </pre>
231+
* @param timeout The timout amount in milliseconds or no timeout if the value
232+
* is zero or less
233+
* @return the amount of free drive space on the drive or volume in kilobytes
234+
* @throws IllegalStateException if an error occurred in initialisation
235+
* @throws IOException if an error occurs when finding the free space
236+
* @since Commons IO 2.0
237+
*/
238+
public static long freeSpaceKb(long timeout) throws IOException {
239+
return freeSpaceKb(new File(".").getAbsolutePath(), timeout);
193240
}
194241

195242
//-----------------------------------------------------------------------
@@ -206,22 +253,24 @@ public static long freeSpaceKb() throws IOException {
206253
* @param path the path to get free space for, not null, not empty on Unix
207254
* @param os the operating system code
208255
* @param kb whether to normalize to kilobytes
256+
* @param timeout The timout amount in milliseconds or no timeout if the value
257+
* is zero or less
209258
* @return the amount of free drive space on the drive or volume
210259
* @throws IllegalArgumentException if the path is invalid
211260
* @throws IllegalStateException if an error occurred in initialisation
212261
* @throws IOException if an error occurs when finding the free space
213262
*/
214-
long freeSpaceOS(String path, int os, boolean kb) throws IOException {
263+
long freeSpaceOS(String path, int os, boolean kb, long timeout) throws IOException {
215264
if (path == null) {
216265
throw new IllegalArgumentException("Path must not be empty");
217266
}
218267
switch (os) {
219268
case WINDOWS:
220-
return (kb ? freeSpaceWindows(path) / 1024 : freeSpaceWindows(path));
269+
return (kb ? freeSpaceWindows(path, timeout) / 1024 : freeSpaceWindows(path, timeout));
221270
case UNIX:
222-
return freeSpaceUnix(path, kb, false);
271+
return freeSpaceUnix(path, kb, false, timeout);
223272
case POSIX_UNIX:
224-
return freeSpaceUnix(path, kb, true);
273+
return freeSpaceUnix(path, kb, true, timeout);
225274
case OTHER:
226275
throw new IllegalStateException("Unsupported operating system");
227276
default:
@@ -235,10 +284,12 @@ long freeSpaceOS(String path, int os, boolean kb) throws IOException {
235284
* Find free space on the Windows platform using the 'dir' command.
236285
*
237286
* @param path the path to get free space for, including the colon
287+
* @param timeout The timout amount in milliseconds or no timeout if the value
288+
* is zero or less
238289
* @return the amount of free drive space on the drive
239290
* @throws IOException if an error occurs
240291
*/
241-
long freeSpaceWindows(String path) throws IOException {
292+
long freeSpaceWindows(String path, long timeout) throws IOException {
242293
path = FilenameUtils.normalize(path);
243294
if (path.length() > 2 && path.charAt(1) == ':') {
244295
path = path.substring(0, 2); // seems to make it work
@@ -248,7 +299,7 @@ long freeSpaceWindows(String path) throws IOException {
248299
String[] cmdAttribs = new String[] {"cmd.exe", "/C", "dir /-c " + path};
249300

250301
// read in the output of the command to an ArrayList
251-
List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE);
302+
List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE, timeout);
252303

253304
// now iterate over the lines we just read and find the LAST
254305
// non-empty line (the free space bytes should be in the last element
@@ -325,10 +376,12 @@ long parseDir(String line, String path) throws IOException {
325376
* @param path the path to get free space for
326377
* @param kb whether to normalize to kilobytes
327378
* @param posix whether to use the posix standard format flag
379+
* @param timeout The timout amount in milliseconds or no timeout if the value
380+
* is zero or less
328381
* @return the amount of free drive space on the volume
329382
* @throws IOException if an error occurs
330383
*/
331-
long freeSpaceUnix(String path, boolean kb, boolean posix) throws IOException {
384+
long freeSpaceUnix(String path, boolean kb, boolean posix, long timeout) throws IOException {
332385
if (path.length() == 0) {
333386
throw new IllegalArgumentException("Path must not be empty");
334387
}
@@ -345,7 +398,7 @@ long freeSpaceUnix(String path, boolean kb, boolean posix) throws IOException {
345398
(flags.length() > 1 ? new String[] {DF, flags, path} : new String[] {DF, path});
346399

347400
// perform the command, asking for up to 3 lines (header, interesting, overflow)
348-
List<String> lines = performCommand(cmdAttribs, 3);
401+
List<String> lines = performCommand(cmdAttribs, 3, timeout);
349402
if (lines.size() < 2) {
350403
// unknown problem, throw exception
351404
throw new IOException(
@@ -407,10 +460,12 @@ long parseBytes(String freeSpace, String path) throws IOException {
407460
*
408461
* @param cmdAttribs the command line parameters
409462
* @param max The maximum limit for the lines returned
463+
* @param timeout The timout amount in milliseconds or no timeout if the value
464+
* is zero or less
410465
* @return the parsed data
411466
* @throws IOException if an error occurs
412467
*/
413-
List<String> performCommand(String[] cmdAttribs, int max) throws IOException {
468+
List<String> performCommand(String[] cmdAttribs, int max, long timeout) throws IOException {
414469
// this method does what it can to avoid the 'Too many open files' error
415470
// based on trial and error and these links:
416471
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
@@ -426,6 +481,9 @@ List<String> performCommand(String[] cmdAttribs, int max) throws IOException {
426481
InputStream err = null;
427482
BufferedReader inr = null;
428483
try {
484+
485+
Thread monitor = ThreadMonitor.start(timeout);
486+
429487
proc = openProcess(cmdAttribs);
430488
in = proc.getInputStream();
431489
out = proc.getOutputStream();
@@ -439,6 +497,9 @@ List<String> performCommand(String[] cmdAttribs, int max) throws IOException {
439497
}
440498

441499
proc.waitFor();
500+
501+
ThreadMonitor.stop(monitor);
502+
442503
if (proc.exitValue() != 0) {
443504
// os command problem, throw exception
444505
throw new IOException(
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.io;
18+
19+
/**
20+
* Monitors a thread, interrupting it of it reaches the specified timout.
21+
* <p>
22+
* This works by sleeping until the specified timout amount and then
23+
* interrupting the thread being monitored. If the thread being monitored
24+
* completes its work before being interrupted, it should <code>interrupt()<code>
25+
* the <i>monitor</i> thread.
26+
* <p>
27+
*
28+
* <pre>
29+
* long timeoutInMillis = 1000;
30+
* try {
31+
* Thread monitor = ThreadMonitor.start(timeoutInMillis);
32+
* // do some work here
33+
* ThreadMonitor.stop(monitor);
34+
* } catch (InterruptedException e) {
35+
* // timed amount was reached
36+
* }
37+
* </pre>
38+
*
39+
* @version $Id$
40+
*/
41+
class ThreadMonitor implements Runnable {
42+
43+
private final Thread thread;
44+
private final long timeout;
45+
46+
/**
47+
* Start monitoring the current thread.
48+
*
49+
* @param timeout The timout amount in milliseconds
50+
* or no timeout if the value is zero or less
51+
* @return The monitor thread or <code>null</code>
52+
* if the timout amount is not greater than zero
53+
*/
54+
public static Thread start(long timeout) {
55+
return start(Thread.currentThread(), timeout);
56+
}
57+
58+
/**
59+
* Start monitoring the specified thread.
60+
*
61+
* @param thread The thread The thread to monitor
62+
* @param timeout The timout amount in milliseconds
63+
* or no timeout if the value is zero or less
64+
* @return The monitor thread or <code>null</code>
65+
* if the timout amount is not greater than zero
66+
*/
67+
public static Thread start(Thread thread, long timeout) {
68+
Thread monitor = null;
69+
if (timeout > 0) {
70+
ThreadMonitor timout = new ThreadMonitor(thread, timeout);
71+
monitor = new Thread(timout, ThreadMonitor.class.getSimpleName());
72+
monitor.setDaemon(true);
73+
monitor.start();
74+
}
75+
return monitor;
76+
}
77+
78+
/**
79+
* Stop monitoring the specified thread.
80+
*
81+
* @param thread The monitor thread, may be <code>null</code>
82+
*/
83+
public static void stop(Thread thread) {
84+
if (thread != null) {
85+
thread.interrupt();
86+
}
87+
}
88+
89+
/**
90+
* Construct and new monitor.
91+
*
92+
* @param thread The thread to monitor
93+
* @param timeout The timout amount in milliseconds
94+
*/
95+
private ThreadMonitor(Thread thread, long timeout) {
96+
this.thread = thread;
97+
this.timeout = timeout;
98+
}
99+
100+
/**
101+
* Sleep until the specified timout amount and then
102+
* interrupt the thread being monitored.
103+
*
104+
* @see Runnable#run()
105+
*/
106+
public void run() {
107+
try {
108+
Thread.sleep(timeout);
109+
thread.interrupt();
110+
} catch (InterruptedException e) {
111+
// timeout not reached
112+
}
113+
}
114+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.io;
18+
19+
import junit.framework.TestCase;
20+
21+
/**
22+
* Tests for {@link ThreadMonitor}.
23+
*/
24+
public class ThreadMonitorTestCase extends TestCase {
25+
26+
27+
public ThreadMonitorTestCase(String name) {
28+
super(name);
29+
}
30+
31+
/**
32+
* Test timeout.
33+
*/
34+
public void testTimeout() {
35+
try {
36+
Thread monitor = ThreadMonitor.start(100);
37+
Thread.sleep(200);
38+
ThreadMonitor.stop(monitor);
39+
fail("Expected InterruptedException");
40+
} catch (InterruptedException e) {
41+
// expected result - timout
42+
}
43+
}
44+
45+
/**
46+
* Test task completed before timeout.
47+
*/
48+
public void testCompletedWithoutTimeout() {
49+
try {
50+
Thread monitor = ThreadMonitor.start(200);
51+
Thread.sleep(100);
52+
ThreadMonitor.stop(monitor);
53+
} catch (InterruptedException e) {
54+
fail("Timed Out");
55+
}
56+
}
57+
58+
/**
59+
* Test No timeout.
60+
*/
61+
public void testNoTimeout() {
62+
63+
// timeout = -1
64+
try {
65+
Thread monitor = ThreadMonitor.start(-1);
66+
assertNull("Timeout -1, Monitor should be null", monitor);
67+
Thread.sleep(100);
68+
ThreadMonitor.stop(monitor);
69+
} catch (Exception e) {
70+
fail("Timeout -1, threw " + e);
71+
}
72+
73+
// timeout = 0
74+
try {
75+
Thread monitor = ThreadMonitor.start(0);
76+
assertNull("Timeout 0, Monitor should be null", monitor);
77+
Thread.sleep(100);
78+
ThreadMonitor.stop(monitor);
79+
} catch (Exception e) {
80+
fail("Timeout 0, threw " + e);
81+
}
82+
}
83+
}
84+

0 commit comments

Comments
 (0)