View Javadoc

1   /*
2    * $Id: VMStatSwappingDetectionThread.java 178 2010-10-31 18:01:20Z roland $
3    * Copyright (C) 2007 Roland Krueger
4    * 
5    * Author: Roland Krueger (www.rolandkrueger.info)
6    *
7    * This file is part of RoKlib.
8    *
9    * This library is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU Lesser General Public License
11   * as published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * This library is distributed in the hope that it will be useful, but
15   * WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this library; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22   * USA
23   */
24  package info.rolandkrueger.roklib.system;
25  
26  import java.io.BufferedReader;
27  import java.io.File;
28  import java.io.IOException;
29  import java.io.InputStreamReader;
30  import java.util.logging.Logger;
31  import java.util.regex.Matcher;
32  import java.util.regex.Pattern;
33  
34  /**
35   * This class is a concrete implementation of the abstract class
36   * {@link MemorySwappingDetectionThread} where the program <code>vmstat</code>
37   * is used for detecting memory swapping. When the thread is started,
38   * <code>vmstat</code> is invoked with an update delay of a configurable number
39   * of seconds. The thread will then evaluate the output of <code>vmstat</code>
40   * upon each update. If the swap in/out column of <code>vmstat</code>'s output
41   * differs from 0 the object registered as {@link MemorySwappingEventListener}
42   * will be notified of that event. <br>
43   * <br>
44   * Note: Detecting memory paging with an external, platform-dependent tool such
45   * as <code>vmstat</code> is of course detrimental to the application's
46   * portability. This strategy was chosen, though, since it offered an easy to
47   * implement method for swapping detection. Unfortunately, the Java language as
48   * yet doesn't offer any general machanisms to detect these memory paging
49   * situations in a portable way. If there are any better ways to cope with that
50   * in the future, an appropriate method can be implemented as a subclass of
51   * {@link MemorySwappingDetectionThread}.
52   * 
53   * @author Roland Krueger
54   */
55  public class VMStatSwappingDetectionThread extends MemorySwappingDetectionThread
56  {
57    protected static Logger sLogger = Logger.getLogger (VMStatSwappingDetectionThread.class
58        .getPackage ().getName ());
59    private final static String CMD_STRING = "%s %d";
60  
61    /**
62     * Regular expression that extracts the swap in and swap out columns of the
63     * <code>vmstat</code> output.
64     */
65    private final static Pattern SWAP_PATTERN = Pattern
66        .compile ("^(?:\\s*\\d+){6}\\s*(\\d+)\\s*(\\d+).*$");
67  
68    private String mVMStatBinaryLocation;
69    private int mSamplingRate;
70    private Process mVMStatProcess;
71    private BufferedReader mProcessReader;
72    private String mLine;
73    private Matcher mMatcher;
74  
75    /**
76     * Constructor.
77     * 
78     * @param listener
79     *          object that implements the {@link MemorySwappingEventListener}
80     *          interface
81     * @param vmstatBinaryLocation
82     *          the location of the <code>vmstat</code> command. This is usually
83     *          <code>/usr/bin/vmstat</code>.
84     * @param samplingRate
85     *          the update delay for <code>vmstat</code>
86     */
87    public VMStatSwappingDetectionThread (MemorySwappingEventListener listener,
88        String vmstatBinaryLocation, int samplingRate)
89    {
90      super (listener);
91      mVMStatBinaryLocation = vmstatBinaryLocation;
92      if (mVMStatBinaryLocation.matches ("\\s"))
93        throw new IllegalArgumentException (
94            "Using a whitespace character in the vmstat binary location string is not allowed.");
95  
96      File vmstatBinary = new File (mVMStatBinaryLocation);
97      if (! vmstatBinary.canRead ())
98        throw new IllegalArgumentException ("Unable to read the given file for the vmstat binary.");
99  
100     mSamplingRate = samplingRate;
101     if (samplingRate <= 0)
102       throw new IllegalArgumentException ("Sampling rate must be at least 1.");
103   }
104 
105   /**
106    * Starts the <code>vmstat</code> command in an own process.
107    * 
108    * @return <code>true</code> if the process was successfully started
109    */
110   public synchronized boolean startProcess ()
111   {
112     // start vmstat and parse its output
113     ProcessBuilder procBuilder = new ProcessBuilder (String.format (CMD_STRING,
114         mVMStatBinaryLocation, mSamplingRate).split (" "));
115     procBuilder.redirectErrorStream (true);
116 
117     try
118     {
119       mVMStatProcess = procBuilder.start ();
120       mProcessReader = new BufferedReader (new InputStreamReader (mVMStatProcess.getInputStream ()));
121     } catch (IOException ioExc)
122     {
123       sLogger.warning ("Unable to start vmstat process with binary :" + mVMStatBinaryLocation);
124       return false;
125     }
126     super.start ();
127     return true;
128   }
129 
130   @Override
131   protected boolean isSwapEventDetected ()
132   {
133     if (mProcessReader == null)
134       throw new IllegalStateException ("Thread must be started with startProcess().");
135     try
136     {
137       mLine = mProcessReader.readLine ();
138       if (mLine == null)
139       {
140         sLogger.info ("vmstat process has finished.");
141         stopThread ();
142         return false;
143       }
144     } catch (IOException e)
145     {
146       stopThread ();
147       return false;
148     }
149 
150     mMatcher = SWAP_PATTERN.matcher (mLine);
151     if (mMatcher.matches ())
152     {
153       if (! mMatcher.group (1).equals ("0") || ! mMatcher.group (2).equals ("0")) return true;
154     }
155 
156     return false;
157   }
158 
159   @Override
160   protected void stopThreadImpl ()
161   {
162     if (mVMStatProcess != null)
163     {
164       sLogger.info ("Stopping vmstat process.");
165       mVMStatProcess.destroy ();
166     }
167   }
168 
169 }