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 int lvmid = 0; 237 238 try { 239 // try 1.4.2 and later format first 240 return Integer.parseInt(file.getName()); 241 } catch (NumberFormatException e) { } 242 243 // now try the 1.4.1 format 244 String name = file.getName(); 245 if (name.startsWith(dirNamePrefix)) { 246 int first = name.indexOf('_'); 247 int last = name.lastIndexOf('_'); 248 try { 249 if (first == last) { 250 return Integer.parseInt(name.substring(first + 1)); 251 } else { 252 return Integer.parseInt(name.substring(first + 1, last)); 253 } 254 } catch (NumberFormatException e) { } 255 } 256 throw new IllegalArgumentException("file name does not match pattern"); 257 } 258 259 /** 260 * Return the name of the temporary directory being searched for 261 * HotSpot PerfData backing store files. 262 * <p> 263 * This method generally returns the value of the java.io.tmpdir 264 * property. However, on some platforms it may return a different 265 * directory, as the JVM implementation may store the PerfData backing 266 * store files in a different directory for performance reasons. 267 * 268 * @return String - the name of the temporary directory. 269 */ 270 public static String getTempDirectory() { 271 return tmpDirName; 272 } 273 274 /** 275 * Return the name of the temporary directory to be searched 276 * for HotSpot PerfData backing store files for a given user. 277 * <p> 278 * This method generally returns the name of a subdirectory of 279 * the directory indicated in the java.io.tmpdir property. However, 280 * on some platforms it may return a different directory, as the 281 * JVM implementation may store the PerfData backing store files 282 * in a different directory for performance reasons. 283 * 284 * @return String - the name of the temporary directory. 285 */ 286 public static String getTempDirectory(String user) { 287 return tmpDirName + dirNamePrefix + user + File.separator; 288 } 289 290 /* 291 * this static initializer would not be necessary if the 292 * Solaris java.io.tmpdir property were set to /tmp by default 293 */ 294 static { 295 /* 296 * Why is java.io.tmpdir on Solaris set to "/var/tmp/" when the 297 * HotSpot JVM os:get_temp_path() method returns "/tmp/" 298 * 299 * Why do Solaris and Windows return a string with a trailing 300 * file separator character where as Linix does not? (this change 301 * seems to have occurred sometime during hopper beta) 302 */ 303 String tmpdir = System.getProperty("java.io.tmpdir"); 304 305 if (tmpdir.compareTo("/var/tmp/") == 0) { 306 /* 307 * shared memory files are created in /tmp. Interestingly, 308 * java.io.tmpdir is set to "/var/tmp/" on Solaris and Linux, 309 * but os::get_temp_directory() is set to "/tmp/" on these 310 * platforms. the java.io.logging packages also makes reference 311 * to java.io.tmpdir. 312 */ 313 tmpdir = "/tmp/"; 314 } 315 316 /* 317 * Assure that the string returned has a trailing File.separator 318 * character. This check was added because the Linux implementation 319 * changed such that the java.io.tmpdir string no longer terminates 320 * with a File.separator character. 321 */ 322 if (tmpdir.lastIndexOf(File.separator) != (tmpdir.length()-1)) { 323 tmpdir = tmpdir + File.separator; 324 } 325 tmpDirName = tmpdir; 326 } 327 }