All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.io.disk.DiskSpaceUtil Maven / Gradle / Ivy

/*
 * Copyright (c) 1997-2009 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.io.disk;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;

/**
 * An utility class to measure the free disk space and the current ulimit.
 * To measure the disk space, it uses the Java 6 method File.getUsableSpace if available,
 * or the command "df" (Unix) or "dir" (Windows).
 * To check the ulimit (Unix only), it runs the command "ulimit -n".
 */
public class DiskSpaceUtil {

    /**
     * If this system property is set, this command line is used to measure the free disk space.
     */
    public static final String COMMAND_DISK_SPACE = "crx.diskSpaceMeasureCommand";

    /**
     * If this system property is set, this command line is used to measure the ulimit.
     */
    public static final String COMMAND_ULIMIT = "crx.ulimitMeasureCommand";

    /**
     * The maximum measure interval  in milliseconds. The default value 60000
     * means measure once a minute.
     * If -1, disk space is never measured.
     */
    public static final String INTERVAL = "crx.diskSpaceMeasureInterval";

    private static final DiskSpaceUtil INSTANCE = new DiskSpaceUtil();

    private long defaultMeasureInterval = Long.decode(System.getProperty(INTERVAL, "60000")).longValue();

    private boolean unsupportedFileUsableSpace;
    private boolean unsupportedDiskFree;

    private long lastMeasuredTime;
    private long lastMeasuredAvailableSpace;

    private int maxOpenFiles;

    /**
     * Get the instance.
     *
     * @return the instance
     */
    public static DiskSpaceUtil getInstance() {
        return INSTANCE;
    }

    /**
     * Get the maximum number of open files for this process.
     * Under Linux it uses the command "ulimit". When running in Windows,
     * the value Integer.MAX_VALUE is returned.
     *
     * @return the maximum number of open files, or Integer.MAX_VALUE
     */
    public int getMaxOpenFiles() {
        if (maxOpenFiles != 0) {
            return maxOpenFiles;
        }
        if (isWindows()) {
            maxOpenFiles = Integer.MAX_VALUE;
        } else {
            String result = "";
            try {
                String command = System.getProperty(COMMAND_ULIMIT, "ulimit,-n");
                List cmd = tokenize(command, ",");
                result = runProcess(cmd).trim();
                maxOpenFiles = Integer.parseInt(result);
            } catch (Exception e) {
                logDebug("ulimit not working: " + result + " " + e);
                maxOpenFiles = Integer.MAX_VALUE;
            }
        }
        logDebug("Maximum number of open files: " + maxOpenFiles);
        return maxOpenFiles;
    }

    /**
     * Get the available disk space in megabytes for the current working
     * directory using the default interval.
     *
     * @return the space or -1 if it can't be measured
     */
    public long getAvailableDiskSpaceMB() {
        return getAvailableDiskSpaceMB(new File("."), defaultMeasureInterval);
    }

    /**
     * Get the available disk space in megabytes for the given directory using
     * the default interval.
     *
     * @param dir the directory
     * @return the space or -1 if it can't be measured
     */
    public long getAvailableDiskSpaceMB(File dir) {
        return getAvailableDiskSpaceMB(dir, defaultMeasureInterval);
    }

    /**
     * Get the available disk space in megabytes for the given directory and
     * using the given interval.
     *
     * @param dir the directory
     * @param interval the interval in milliseconds
     * @return the space in megabytes or -1 if it can't be measured;
     *  Integer.MAX_VALUE if the interval is smaller than 0
     */
    public long getAvailableDiskSpaceMB(File dir, long interval) {
        if (interval < 0) {
            return Integer.MAX_VALUE;
        }
        long now = System.currentTimeMillis();
        if (lastMeasuredTime == 0 || now > lastMeasuredTime + interval) {
            lastMeasuredTime = now;
            lastMeasuredAvailableSpace = getUsableSpace(dir);
        }
        return lastMeasuredAvailableSpace;
    }

    /**
     * Call the Java method File.getUsableSpace if available. If not, call
     * diskFree.
     *
     * @param dir the directory
     * @return -1 or the number of megabytes available
     */
    private long getUsableSpace(File dir) {
        // dir.getUsableSpace();
        if (!unsupportedFileUsableSpace) {
            try {
                Method usable = File.class.getMethod("getUsableSpace", new Class[0]);
                long space = ((Long) usable.invoke(dir, new Object[0])).longValue();
                if (space == 0) {
                    return -1;
                }
                space /= 1024 * 1024;
                logDebug("Usable disk space: " + space + " MB (using \"File.getUsableSpace\")");
                return space;
            } catch (Exception e) {
                logDebug("Method File.getUsableSpace is not supported: " + e);
            }
        }
        unsupportedFileUsableSpace = true;
        return diskFree(dir);
    }

    /**
     * Check whether this is a Windows system.
     *
     * @return true if it is
     */
    private boolean isWindows() {
        return System.getProperty("os.name").startsWith("Windows");
    }

    /**
     * Call the Unix command "df".
     *
     * @param dir the directory
     * @return -1 or the number of megabytes available
     */
    private long diskFree(File dir) {
        if (!unsupportedDiskFree) {
            try {
                String defaultCommand;
                boolean isWindows;
                if (isWindows()) {
                    defaultCommand = "cmd,/c,dir,${dir}";
                    isWindows = true;
                } else {
                    defaultCommand = "df,-m,${dir}";
                    isWindows = false;
                }
                String command = System.getProperty(COMMAND_DISK_SPACE, defaultCommand);
                List cmd = tokenize(command, ",");
                for (int i = 0; i < cmd.size(); i++) {
                    if ("${dir}".equals(cmd.get(i))) {
                        cmd.set(i, dir.getAbsolutePath());
                    }
                }
                String result = runProcess(cmd);
                long x = parseResult(result, isWindows);
                if (x != -1) {
                    return x;
                }
            } catch (Exception e) {
                logDebug("df / dir not working: " + e);
            }
        }
        unsupportedDiskFree = true;
        return -1;
    }

    /**
     * Try to parse the result of "df -m ." / "dir" and return the free disk
     * space in MB.
     *
     * @param result the result of "df -m ." / "dir".
     * @param isWindows whether "dir" was executed
     * @return the free disk space or -1 if parsing failed
     */
    private long parseResult(String result, boolean isWindows) {
        String[] lines = result.split("\n");
        if (!isWindows && lines.length > 1) {
            int index = 0;
            boolean found = false;
            List lineList = tokenize(lines[0], " ");
            for (int i = 0; i < lineList.size(); i++) {
                String h = (String) lineList.get(i);
                if (h.startsWith("Avail")) {
                    found = true;
                    break;
                }
                index++;
            }
            if (found) {
                if (lines.length > 2 && !lines[2].trim().equals(lines[2])) {
                    // the values are actually on the third line, because
                    // the path is too long
                    lines[1] = lines[1].trim() + " " + lines[2].trim();
                }
                List data = tokenize(lines[1], " ");
                if (data.size() >= index) {
                    long space = Long.parseLong((String) data.get(index));
                    logDebug("Usable disk space: " + space + " MB (using \"df\")");
                    return space;
                }
            }
        }
        if (isWindows && lines.length > 0) {
            String line = lines[lines.length - 1];
            int end = -1;
            int i = line.length() - 1;
            for (; i >= 0; i--) {
                if (Character.isDigit(line.charAt(i))) {
                    end = i;
                    break;
                }
            }
            for (; i >= 0; i--) {
                if (line.charAt(i) == ' ') {
                    break;
                }
            }
            StringBuilder buff = new StringBuilder();
            for (; i <= end; i++) {
                char c = line.charAt(i);
                if (Character.isDigit(c)) {
                    buff.append(line.charAt(i));
                }
            }
            String s = buff.toString();
            long space = Long.parseLong(s) / 1024 / 1024;
            logDebug("Usable disk space: " + space + " MB (using \"dir\")");
            return space;
        }
        return -1;
    }

    /**
     * Tokenize a string.
     *
     * @param s the string
     * @param delimiters the delimiters
     * @return the array list
     */
    private static List tokenize(String s, String delimiters) {
        if (s == null || s.length() == 0) {
            return Collections.emptyList();
        }
        ArrayList list = new ArrayList();
        StringTokenizer tokenizer = new StringTokenizer(s, delimiters);
        while (tokenizer.hasMoreTokens()) {
            list.add(tokenizer.nextToken());
        }
        return list;
    }

    /**
     * Run a process and return the result.
     *
     * @param args the command line
     * @return the result
     */
    private String runProcess(List args) {
        try {
            logDebug("runProcess args: " + args);
            String[] argList = new String[args.size()];
            args.toArray(argList);
            Process p = Runtime.getRuntime().exec(argList);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            Thread threadOut = copyInThread(p.getInputStream(), out, false);
            Thread threadErr = copyInThread(p.getErrorStream(), out, false);
            p.waitFor();
            int exitValue = p.exitValue();
            threadOut.join();
            threadErr.join();
            String result = new String(out.toByteArray());
            logDebug("exitValue=" + exitValue);
            logDebug(result);
            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void logDebug(String message) {
        // System.out.println(message);
    }

    private Thread copyInThread(final InputStream in, final OutputStream out, final boolean toSysOut) {
        Thread t = new Thread() {
            public void run() {
                try {
                    while (true) {
                        int x = in.read();
                        if (x < 0) {
                            return;
                        }
                        if (out != null) {
                            out.write(x);
                            if (toSysOut) {
                                System.out.print((char) x);
                            }
                        }
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };
        t.start();
        return t;
    }

    public long getDefaultMeasureInterval() {
        return this.defaultMeasureInterval;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy