/* * Copyright 2008 the original author or authors. * * Licensed 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.rioproject.impl.system.measurable.disk; import org.rioproject.impl.exec.Util; import org.rioproject.impl.system.OperatingSystemType; import org.rioproject.impl.system.measurable.MeasurableMonitor; import org.rioproject.impl.system.measurable.SigarHelper; import org.rioproject.system.measurable.disk.DiskSpaceUtilization; import org.rioproject.watch.ThresholdValues; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; /** * The <code>DiskSpaceMonitor</code> monitors disk space usage. This class * uses either Hyperic SIGAR, or operations system specific utilities (like df) * to obtain this information. The use of SIGAR is preferred, and if not * available will use external <tt>df</t> exec by forking a process and parsing * it's results. * * @author Dennis Reedy */ public class DiskSpaceMonitor implements MeasurableMonitor<DiskSpaceUtilization> { static Logger logger = LoggerFactory.getLogger("org.rioproject.system.measurable.disk"); private String id; private ThresholdValues tVals; private SigarHelper sigar; private String fileSystem = File.separator; private final Object updateLock = new Object(); public DiskSpaceMonitor() { sigar = SigarHelper.getInstance(); } public void setID(String id) { this.id = id; } public void setThresholdValues(ThresholdValues tVals) { this.tVals = tVals; } public void setFileSystemToMonitor(String fileSystem) { this.fileSystem = fileSystem; } public DiskSpaceUtilization getMeasuredResource() { DiskSpaceUtilization dsu; if(sigar==null) { dsu = getDiskSpaceUtilization(); } else { dsu = getDiskSpaceUtilizationUsingSigar(); } return dsu; } /* (non-Javadoc) * @see org.rioproject.system.measurable.MeasurableMonitor#terminate() */ public void terminate() { /* implemented for interface compliance */ } private DiskSpaceUtilization getDiskSpaceUtilizationUsingSigar() { DiskSpaceUtilization dsu; try { /* FileSystemUsage fUse = sigar.getFileSystemUsage(File.separator); double available = fUse.getFree()*1024; double used = fUse.getUsed()*1024; double total = fUse.getTotal()*1024; */ double available = sigar.getFileSystemFree(fileSystem)*1024; double used = sigar.getFileSystemUsed(fileSystem)*1024; double total = sigar.getFileSystemTotal(fileSystem)*1024; dsu = new DiskSpaceUtilization(id, used, available, total, sigar.getFileSystemUsedPercent(fileSystem), tVals); } catch (Exception e) { logger.warn("SIGAR exception getting FileSystemUsage", e); dsu = new DiskSpaceUtilization(id, -1, tVals); } return dsu; } private DiskSpaceUtilization getDiskSpaceUtilization() { double used = 0; double available = 0; if (!OperatingSystemType.isWindows()) { Process process = null; DFOutputParser outputParser = null; try { synchronized (updateLock) { try { process = Runtime.getRuntime().exec("df -k"); outputParser = new DFOutputParser(process.getInputStream()); outputParser.start(); updateLock.wait(); } catch (InterruptedException e) { logger.warn("Waiting on updateLock", e); } } used = outputParser.getUsed() * 1024; available = outputParser.getAvailable() * 1024; } catch (IOException e) { logger.warn("Executing or spawning [df-k]", e); } finally { if (outputParser != null) outputParser.interrupt(); if (process != null) { Util.close(process.getOutputStream()); Util.close(process.getInputStream()); Util.close(process.getErrorStream()); process.destroy(); } } } double capacity = used + available; double u = used/capacity; BigDecimal bd = new BigDecimal(u).setScale(2, RoundingMode.HALF_EVEN); double utilization = bd.doubleValue(); return (new DiskSpaceUtilization(id, used, available, capacity, utilization, tVals)); } /** * Class to parse output from the df -k command */ class DFOutputParser extends Thread { InputStream in; double used; double available; public DFOutputParser(InputStream in) { this.in = in; } double getAvailable() { return (available); } double getUsed() { return (used); } public void run() { BufferedReader br = null; try { String fileSep = System.getProperty("file.separator"); List<String> list = new ArrayList<String>(); InputStreamReader isr = new InputStreamReader(in); br = new BufferedReader(isr); String line; while ((line = br.readLine()) != null && !isInterrupted()) { if (line.startsWith("Filesystem")) continue; StringTokenizer st = new StringTokenizer(line, " "); while (st.hasMoreTokens()) list.add(st.nextToken()); /* * The following 2 lines address the problem when the * DiskSpaceMonitor parses the result of a 'df -k' and the * local system has NFS mounted filesystems. On Linux (and * possibly other OS's), if the name of filesystem (the * first column) would run into the second column, then the * df command prints the filesystem on the first line (by * itself) and the remaining fields are printed on the * second line. After tokenizing the df output, the run() * method does a 'list.get(5)'. Unfortunately, list * sometimes only contains 1 element. */ if (list.size() == 1) continue; /* * Get the mount point */ String mountPoint = list.get(list.size()-1); if (mountPoint.equals(fileSep)) { used = Double.parseDouble(list.get(2)); available = Double.parseDouble(list.get(3)); } list.clear(); } } catch (IOException e) { logger.info("Grabbing output of df -k", e); } finally { try { if (br != null) br.close(); } catch (IOException e) { e.printStackTrace(); } try { in.close(); } catch (IOException e) { e.printStackTrace(); } synchronized (updateLock) { updateLock.notifyAll(); } } } } }