1 /* 2 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.jvmstat.perfdata.monitor.protocol.local; 27 28 import java.io.File; 29 import java.io.FilenameFilter; 30 31 /** 32 * Class to provide translations from the local Vm Identifier 33 * name space into the file system name space and vice-versa. 34 * <p> 35 * Provides a factory for creating a File object to the backing 36 * store file for instrumentation shared memory region for a JVM 37 * identified by its Local Java Virtual Machine Identifier, or 38 * <em>lvmid</em>. 39 * 40 * @author Brian Doherty 41 * @since 1.5 42 * @see java.io.File 43 */ 44 public class PerfDataFile { 45 private PerfDataFile() { }; 46 47 /** 48 * The name of the of the system dependent temporary directory 49 */ 50 public static final String tmpDirName; 51 52 /** 53 * The file name prefix for PerfData shared memory files. 54 * <p> 55 * This prefix must be kept in sync with the prefix used by the JVM. 56 */ 57 public static final String dirNamePrefix = "hsperfdata_"; 58 59 /** 60 * The directory name pattern for the user directories. 61 */ 62 public static final String userDirNamePattern = "hsperfdata_\\S*"; 63 64 /** 65 * The file name pattern for PerfData shared memory files. 66 * <p> 67 * This pattern must be kept in synch with the file name pattern 68 * used by the 1.4.2 and later HotSpot JVM. 69 */ 70 public static final String fileNamePattern = "^[0-9]+$"; 71 72 /** 73 * The file name pattern for 1.4.1 PerfData shared memory files. 74 * <p> 75 * This pattern must be kept in synch with the file name pattern 76 * used by the 1.4.1 HotSpot JVM. 77 */ 78 public static final String tmpFileNamePattern = 79 "^hsperfdata_[0-9]+(_[1-2]+)?$"; 80 81 82 /** 83 * Get a File object for the instrumentation backing store file 84 * for the JVM identified by the given local Vm Identifier. 85 * <p> 86 * This method looks for the most up to date backing store file for 87 * the given <tt>lvmid</tt>. It will search all the user specific 88 * directories in the temporary directory for the host operating 89 * system, which may be influenced by platform specific environment 90 * variables. 91 * 92 * @param lvmid the local Java Virtual Machine Identifier for the target 93 * @return File - a File object to the backing store file for the named 94 * shared memory region of the target JVM. 95 * @see java.io.File 96 * @see #getTempDirectory() 97 */ 98 public static File getFile(int lvmid) { 99 if (lvmid == 0) { 100 /* 101 * lvmid == 0 is used to indicate the current Java Virtual Machine. 102 * If the SDK provided an API to get a unique Java Virtual Machine 103 * identifier, then a filename could be constructed with that 104 * identifier. In absence of such an api, return null. 105 */ 106 return null; 107 } 108 109 /* 110 * iterate over all files in all directories in tmpDirName that 111 * match the file name patterns. 112 */ 113 File tmpDir = new File(tmpDirName); 114 String[] files = tmpDir.list(new FilenameFilter() { 115 public boolean accept(File dir, String name) { 116 if (!name.startsWith(dirNamePrefix)) { 117 return false; 118 } 119 File candidate = new File(dir, name); 120 return ((candidate.isDirectory() || candidate.isFile()) 121 && candidate.canRead()); 122 } 123 }); 124 125 long newestTime = 0; 126 File newest = null; 127 128 for (int i = 0; i < files.length; i++) { 129 File f = new File(tmpDirName + files[i]); 130 File candidate = null; 131 132 if (f.exists() && f.isDirectory()) { 133 /* 134 * found a directory matching the name patterns. This 135 * is a 1.4.2 hsperfdata_<user> directory. Check for 136 * file named <lvmid> in that directory 137 */ 138 String name = Integer.toString(lvmid); 139 candidate = new File(f.getName(), name); 140 141 } else if (f.exists() && f.isFile()) { 142 /* 143 * found a file matching the name patterns. This 144 * is a 1.4.1 hsperfdata_<lvmid> file. 145 */ 146 candidate = f; 147 148 } else { 149 // unexpected - let conditional below filter this one out 150 candidate = f; 151 } 152 153 if (candidate.exists() && candidate.isFile() 154 && candidate.canRead()) { 155 long modTime = candidate.lastModified(); 156 if (modTime >= newestTime) { 157 newestTime = modTime; 158 newest = candidate; 159 } 160 } 161 } 162 return newest; 163 } 164 165 /** 166 * Return the File object for the backing store file for the specified Java 167 * Virtual Machine. 168 * <p> 169 * This method looks for the most up to date backing store file for 170 * the JVM identified by the given user name and lvmid. The directory 171 * searched is the temporary directory for the host operating system, 172 * which may be influenced by environment variables. 173 * 174 * @param user the user name 175 * @param lvmid the local Java Virtual Machine Identifier for the target 176 * @return File - a File object to the backing store file for the named 177 * shared memory region of the target JVM. 178 * @see java.io.File 179 * @see #getTempDirectory() 180 */ 181 public static File getFile(String user, int lvmid) { 182 if (lvmid == 0) { 183 /* 184 * lvmid == 0 is used to indicate the current Java Virtual Machine. 185 * If the SDK provided an API to get a unique Java Virtual Machine 186 * identifier, then a filename could be constructed with that 187 * identifier. In absence of such an api, return null. 188 */ 189 return null; 190 } 191 192 // first try for 1.4.2 and later JVMs 193 String basename = getTempDirectory(user) + Integer.toString(lvmid); 194 File f = new File(basename); 195 196 if (f.exists() && f.isFile() && f.canRead()) { 197 return f; 198 } 199 200 // No hit on 1.4.2 JVMs, try 1.4.1 files 201 long newestTime = 0; 202 File newest = null; 203 for (int i = 0; i < 2; i++) { 204 if (i == 0) { 205 basename = getTempDirectory() + Integer.toString(lvmid); 206 } else { 207 basename = getTempDirectory() + Integer.toString(lvmid) 208 + Integer.toString(i); 209 } 210 211 f = new File(basename); 212 213 if (f.exists() && f.isFile() && f.canRead()) { 214 long modTime = f.lastModified(); 215 if (modTime >= newestTime) { 216 newestTime = modTime; 217 newest = f; 218 } 219 } 220 } 221 return newest; 222 } 223 224 /** 225 * Method to extract a local Java Virtual Machine Identifier from the 226 * file name of the given File object. 227 * 228 * @param file A File object representing the name of a 229 * shared memory region for a target JVM 230 * @return int - the local Java Virtual Machine Identifier for the target 231 * associated with the file 232 * @throws java.lang.IllegalArgumentException Thrown if the file name 233 * does not conform to the expected pattern 234 */ 235 public static int getLocalVmId(File file) { 236 try { 237 // try 1.4.2 and later format first 238 return Integer.parseInt(file.getName()); 239 } catch (NumberFormatException e) { } 240 241 // now try the 1.4.1 format 242 String name = file.getName(); 243 if (name.startsWith(dirNamePrefix)) { 244 int first = name.indexOf('_'); 245 int last = name.lastIndexOf('_'); 246 try { 247 if (first == last) { 248 return Integer.parseInt(name.substring(first + 1)); 249 } else { 250 return Integer.parseInt(name.substring(first + 1, last)); 251 } 252 } catch (NumberFormatException e) { } 253 } 254 throw new IllegalArgumentException("file name does not match pattern"); 255 } 256 257 /** 258 * Return the name of the temporary directory being searched for 259 * HotSpot PerfData backing store files. 260 * <p> 261 * This method generally returns the value of the java.io.tmpdir 262 * property. However, on some platforms it may return a different 263 * directory, as the JVM implementation may store the PerfData backing 264 * store files in a different directory for performance reasons. 265 * 266 * @return String - the name of the temporary directory. 267 */ 268 public static String getTempDirectory() { 269 return tmpDirName; 270 } 271 272 /** 273 * Return the name of the temporary directory to be searched 274 * for HotSpot PerfData backing store files for a given user. 275 * <p> 276 * This method generally returns the name of a subdirectory of 277 * the directory indicated in the java.io.tmpdir property. However, 278 * on some platforms it may return a different directory, as the 279 * JVM implementation may store the PerfData backing store files 280 * in a different directory for performance reasons. 281 * 282 * @return String - the name of the temporary directory. 283 */ 284 public static String getTempDirectory(String user) { 285 return tmpDirName + dirNamePrefix + user + File.separator; 286 } 287 288 static { 289 /* 290 * For this to work, the target VM and this code need to use 291 * the same directory. Instead of guessing which directory the 292 * VM is using, we will ask. 293 */ 294 String tmpdir = sun.misc.VMSupport.getVMTemporaryDirectory(); 295 296 /* 297 * Assure that the string returned has a trailing File.separator 298 * character. This check was added because the Linux implementation 299 * changed such that the java.io.tmpdir string no longer terminates 300 * with a File.separator character. 301 */ 302 if (tmpdir.lastIndexOf(File.separator) != (tmpdir.length()-1)) { 303 tmpdir = tmpdir + File.separator; 304 } 305 tmpDirName = tmpdir; 306 } 307 }