View Javadoc

1   /*
2    * Copyright (c) 2003 Peter Antman, Teknik i Media  <peter.antman@tim.se>
3    *
4    * $Id: Drive.java,v 1.1.1.1 2004/05/19 12:33:41 pra Exp $
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2 of the License, or (at your option) any later version
10   * 
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package org.backsource.alert.util;
21  import java.io.File;
22  import java.io.BufferedReader;
23  import java.io.InputStreamReader;
24  import java.io.IOException;
25  import java.util.StringTokenizer;
26  import java.util.ArrayList;
27  
28  import org.apache.log4j.Logger;
29  
30  /***
31   * Represent a drive/partition in the filesystem.
32   *
33   * <p>A drive is a simple representation of a partition/harddisk/filesystem 
34   * that is available on the computer. Through it one can get the partition/harddisk name, such as /dev/hda1 or C:, or the file system name, such as / or C:. 
35   * The total size and how much of it that is used is also available as bytes.</p>
36   * <p>The only way to get one or more Drives is through the two static methods: {@link #getDrives()} and {@link #getDrive(String driveName)}.</p>
37   * <p>Currently this is done through the use of the external command df, which is supported on LINUX, FreeBSD, Mac OS X and Windows plattforms wich has <a href="http://www.cygwin.com/">CYGWIN</a> installed.</p>
38   * <h3>Examples.</h3>
39   * <p>
40   <pre>
41     // Get a single drive
42     Drive d = Drive.getDrive("/");
43     if ( d.percentUsed() > 93) {
44        System.err.println("Diskspace is soon full for normal users, percent used = " + d.percentUsed()+"%"); 
45     } // end of if ()
46    </pre>
47    </p>
48    <p>
49    <pre>
50      // Get all drives
51      Drive[] d = Drive.getDrives();
52    </pre>
53    </p>
54   * <p><b>Depends on: log4j.jar</b></p>
55   *
56   * @author <a href="mailto:pra@tim.se">Peter Antman</a>
57   * @version $Revision: 1.1.1.1 $
58   */
59  public class Drive {
60     private static final Logger log = Logger.getLogger(Drive.class);
61     private String driveName;
62     private long size;
63     private long used;
64     private int percentUsed = -1;
65     private File file;
66     
67     private Drive (){
68        
69  
70     }
71  
72     /***
73      * Get the name of the device.
74      */
75     public String getDriveName() {
76        return driveName;
77     }
78     
79     /***
80      * Get size of device in bytes.
81      */
82     public long getSize() {
83        return size;
84     }
85     
86     /***
87      * Get number of bytes used.
88      */
89     public long getUsed() {
90        return used;
91     }
92  
93     /***
94      * Get percent of disk used.
95      */
96     public int getPercentUsed() {
97        if ( percentUsed != -1) {
98           return percentUsed;
99        } else {
100          if ( used == 0) {
101             return 0;
102          } else if ( size == 0) {
103             return 100;//;-)
104          } else {
105             return (int)(used/size)*100;
106 
107          } // end of else
108          
109       } // end of else
110       
111    }
112 
113    /***
114     * Get a file represenation of device, i.e filsystem mounted on device.
115     */
116    public File getFile() {
117       return file;
118    }
119 
120    /***
121     * Get a string rep of device, looking like a df line.
122     */
123    public String toString() {
124       return driveName + " " +size + " " + used + " " +percentUsed +"% " + file;
125    }
126 
127    /***
128     * Get a drive by its name, may be either device name or mount point name.
129     * @return a Drive or null if no drive by the given name exists.
130     */
131    public static Drive getDrive(String driveName) throws IOException{
132       Drive[] drives = getDrives(driveName);
133       if ( drives.length == 0) {
134          return null;
135       } else {
136          return drives[0];
137       } // end of else
138       
139    }
140 
141    /***
142     * Get all drives available on the system.
143     * @return an array of Drives, which is empty if no drives are found.
144     */
145    public static Drive[] getDrives() throws IOException{
146       return getDrives(null);
147    }
148 
149    /***
150     * main entry for internal methods, delegates to correct os methods.
151     * If driveName is null, all drives on system is returned.
152     */
153    protected static Drive[] getDrives(String driveName) throws IOException{
154 
155       String os = System.getProperty("os.name");
156       
157       Drive[] drives = null;
158       if( os.equalsIgnoreCase("LINUX") 
159           || os.equalsIgnoreCase("FreeBSD")
160           || os.equalsIgnoreCase("Mac OS X") 
161           || os.equalsIgnoreCase("SunOS")
162           )
163          {
164             drives = df(driveName);
165          }
166       else if( os.equalsIgnoreCase("Windows 98") 
167                || os.equalsIgnoreCase("Windows NT") 
168                || os.equalsIgnoreCase("Windows 2000") 
169                || os.equalsIgnoreCase("Windows XP")
170                )
171          {
172             if ( hasCygwin() ) {
173                drives = getWinDrives(driveName);
174             } else {
175                throw new IOException("Currently only supported on windows plattforms wich has cygwin installed");
176             } // end of else
177 
178          }else {
179             throw new IOException("OS not supported: " + os);
180          } // end of else
181       
182    
183       return (drives != null ? drives : new Drive[0]);
184    }
185 
186    /***
187     * Run the df command an create drives.
188     * @param drive get single drive (by device name or file system name) or if null all drives.
189     */
190    protected static Drive[] df(String drive) throws IOException{
191       String cmd = drive == null ? "df -k" : "df -k " + drive;
192       Process cpProcess = Runtime.getRuntime().exec( cmd );
193       // prepare and parse the result
194       // axel@lachs:~ > df /dev/sda3
195       // Filesystem           1k-blocks      Used Available Use% Mounted on
196       // /dev/sda3              4022400   1672696   2145368  44% /
197       BufferedReader reader = new BufferedReader( new InputStreamReader( cpProcess.getInputStream() ));
198 
199       String line = reader.readLine(); // throw away the first line
200       ArrayList drives = new ArrayList();
201       do {
202          line = reader.readLine();
203          if ( line != null) {
204             Drive d = null;
205             // TODO could be maed more generic, dont stop parsning until all
206             // tokens found.
207             try {
208                 d = getDriveFromDf(line);
209             } catch (java.util.NoSuchElementException e) {
210                // Long deviced leeds to braked lines!!!
211                // try again
212                try {
213                   line = line+reader.readLine();
214                   d = getDriveFromDf(line);
215                } catch (java.util.NoSuchElementException  ne) {
216                   // Fuck we give up
217                   throw new IOException("Could not parse lines correct:" + line);
218                } // end of try-catch
219  
220             } // end of try-catch
221             if ( d != null) {
222                drives.add( d );
223             }else {
224                throw new IOException("Could not parse lines correct:" + line);
225             } // end of else
226             
227 
228          } // end of if ()
229          
230       }while (line != null);
231       
232       return (Drive[])drives.toArray( new Drive[ drives.size() ] );
233 
234    }
235    
236    /***
237     * Parse a df line output and instantiate a Drive from it.
238     */
239    protected static Drive getDriveFromDf(String line) {
240       log.debug("DF parsing line " + line);
241       StringTokenizer tokenizer = new StringTokenizer(line);
242       String driveName= tokenizer.nextToken().trim();	// device
243       
244       String size = tokenizer.nextToken().trim();	// total size
245       String used = tokenizer.nextToken().trim();	// used
246       tokenizer.nextToken();	// throw away available
247       String percent = tokenizer.nextToken().trim();   //Use%
248       String file = tokenizer.nextToken().trim();
249 
250       Drive d = new Drive();
251       d.driveName = driveName;
252 
253       // we get k blocks, but need bytes
254       d.size = 1024 * Long.parseLong(size); // get numeric value
255       d.used = 1024 * Long.parseLong(used); // get numeric value
256      
257       d.percentUsed = Integer.valueOf( percent.substring(0, percent.indexOf("%"))).intValue();
258       d.file = new File(file);
259       log.debug("Constructed Drive " + d);
260       return d;
261    }
262 
263    /***
264     * Entry for windows.
265     * <p>Does not use df directly since cygwin df emulates a lot of
266     * extra drives to help unix commands. The file system will be constructed
267     * from the driveName.</p>
268     * 
269     */
270    protected static Drive[] getWinDrives(String drive) throws IOException {
271       File[] roots;
272       if ( drive != null) {
273          roots = new File[]{ new File(drive) };
274       } else {
275          
276          roots = File.listRoots();
277       } // end of else
278       ArrayList drives = new ArrayList();
279       for ( int i = 0; i<roots.length;i++) {
280          Drive d = dir(roots[i]);
281          if ( d != null) {
282             // Fix so that correct File is used
283             d.file = roots[i];
284             drives.add(d);
285          } // end of if ()
286          
287       } // end of for ()
288       return (Drive[])drives.toArray( new Drive[ drives.size() ] );
289    }
290 
291    /***
292     * Dir emultion on win with df installed.
293     */
294    protected static Drive dir(File drive) throws IOException{
295       Drive[] drives = df(drive.getPath());
296       if ( drives.length == 0) {
297          return null;
298       } else {
299          return drives[0];
300       } // end of else
301       
302       
303    }
304 
305    /***
306     * Check if system has Cygwin installed (does a uname).
307     */
308    protected static boolean hasCygwin() {
309       boolean hasCygwin = false;
310       try {
311          Process cpProcess = Runtime.getRuntime().exec( "uname" );
312          BufferedReader reader = new BufferedReader( new InputStreamReader( cpProcess.getInputStream() ));
313          int exit = cpProcess.waitFor();
314          if ( exit == 0) {
315             // Seems as if uname at least worked
316             String line = reader.readLine();
317             if ( line != null && line.startsWith("CYGWIN") ) {
318                hasCygwin = true;
319             } // end of if ()
320             
321          } else {
322             log.debug("uname process ended with non zero status");
323          } // end of else
324          
325       } catch (IOException e) {
326          log.debug("IOException in running uname " + e);
327          // NOOP, just false
328       } catch (InterruptedException e) {
329          log.debug("uname Process thread interrupted" + e);
330          // NOOP, just false
331       } // end of catch
332       
333       return hasCygwin;
334       
335    }
336 
337    
338 
339    public static void main (String[] args) {
340       try {
341          Drive[] drives = Drive.getDrives();
342          for ( int i = 0;i<drives.length;i++) {
343             System.out.println("Drive: " +drives[i] );
344          } // end of for ()
345 
346          // Take one and test, first by drive, then by file ;-)
347          if ( drives.length > 0) {
348             Drive t = drives[0];
349             
350             Drive d = Drive.getDrive( t.getDriveName());
351             System.out.println("Drive by driveName: " +d );
352             if ( !d.getDriveName().equals( t.getDriveName() ) ) {
353                throw new Exception("Could not get drive by driveName");
354             } // end of if ()
355             
356             d = Drive.getDrive( t.getFile().getPath() );
357             System.out.println("Drive by file: " +d );
358             if ( !d.getFile().getPath().equals( t.getFile().getPath()) ) {
359                throw new Exception("Could not get drive by file");
360             } // end of if ()
361 
362          } // end of if ()
363          
364          
365       } catch (Exception e) {
366          e.printStackTrace();
367       } // end of try-catch
368       
369    } // end of main ()
370    
371 }// Drive