1 /* 2 * Copyright (c) 2016, 2018, 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 jdk.jfr.internal.tool; 27 28 import java.io.File; 29 import java.io.FileNotFoundException; 30 import java.io.IOError; 31 import java.io.IOException; 32 import java.io.PrintStream; 33 import java.io.RandomAccessFile; 34 import java.nio.file.Files; 35 import java.nio.file.InvalidPathException; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.Deque; 41 import java.util.List; 42 43 abstract class Command { 44 public final static String title = "Tool for working with Flight Recorder files (.jfr)"; 45 private final static Command HELP = new Help(); 46 private final static List<Command> COMMANDS = createCommands(); 47 createCommands()48 private static List<Command> createCommands() { 49 List<Command> commands = new ArrayList<>(); 50 commands.add(new Print()); 51 commands.add(new Metadata()); 52 commands.add(new Summary()); 53 commands.add(new Assemble()); 54 commands.add(new Disassemble()); 55 commands.add(new Version()); 56 commands.add(HELP); 57 return Collections.unmodifiableList(commands); 58 } 59 displayHelp()60 static void displayHelp() { 61 System.out.println(title); 62 System.out.println(); 63 displayAvailableCommands(System.out); 64 } 65 getName()66 abstract public String getName(); 67 getDescription()68 abstract public String getDescription(); 69 execute(Deque<String> argList)70 abstract public void execute(Deque<String> argList) throws UserSyntaxException, UserDataException; 71 getTitle()72 protected String getTitle() { 73 return getDescription(); 74 } 75 displayAvailableCommands(PrintStream stream)76 static void displayAvailableCommands(PrintStream stream) { 77 boolean first = true; 78 for (Command c : Command.COMMANDS) { 79 if (!first) { 80 System.out.println(); 81 } 82 displayCommand(stream, c); 83 stream.println(" " + c.getDescription()); 84 first = false; 85 } 86 } 87 displayCommand(PrintStream stream, Command c)88 protected static void displayCommand(PrintStream stream, Command c) { 89 boolean firstSyntax = true; 90 String alias = buildAlias(c); 91 String initial = " jfr " + c.getName(); 92 for (String syntaxLine : c.getOptionSyntax()) { 93 if (firstSyntax) { 94 if (syntaxLine.length() != 0) { 95 stream.println(initial + " " + syntaxLine + alias); 96 } else { 97 stream.println(initial + alias); 98 } 99 } else { 100 for (int i = 0; i < initial.length(); i++) { 101 stream.print(" "); 102 } 103 stream.println(" " + syntaxLine); 104 } 105 firstSyntax = false; 106 } 107 } 108 buildAlias(Command c)109 private static String buildAlias(Command c) { 110 List<String> aliases = c.getAliases(); 111 if (aliases.isEmpty()) { 112 return ""; 113 } 114 StringBuilder sb = new StringBuilder(); 115 if (aliases.size() == 1) { 116 sb.append(" (alias "); 117 sb.append(aliases.get(0)); 118 sb.append(")"); 119 return sb.toString(); 120 } 121 sb.append(" (aliases "); 122 for (int i = 0; i< aliases.size(); i ++ ) { 123 sb.append(aliases.get(i)); 124 if (i < aliases.size() -1) { 125 sb.append(", "); 126 } 127 } 128 sb.append(")"); 129 return sb.toString(); 130 } 131 getCommands()132 public static List<Command> getCommands() { 133 return COMMANDS; 134 } 135 valueOf(String commandName)136 public static Command valueOf(String commandName) { 137 for (Command command : COMMANDS) { 138 if (command.getName().equals(commandName)) { 139 return command; 140 } 141 } 142 return null; 143 } 144 getOptionSyntax()145 public List<String> getOptionSyntax() { 146 return Collections.singletonList(""); 147 } 148 displayOptionUsage(PrintStream stream)149 public void displayOptionUsage(PrintStream stream) { 150 } 151 acceptOption(Deque<String> options, String expected)152 protected boolean acceptOption(Deque<String> options, String expected) throws UserSyntaxException { 153 if (expected.equals(options.peek())) { 154 if (options.size() < 2) { 155 throw new UserSyntaxException("missing value for " + options.peek()); 156 } 157 options.remove(); 158 return true; 159 } 160 return false; 161 } 162 warnForWildcardExpansion(String option, String filter)163 protected void warnForWildcardExpansion(String option, String filter) throws UserDataException { 164 // Users should quote their wildcards to avoid expansion by the shell 165 try { 166 if (!filter.contains(File.pathSeparator)) { 167 Path p = Path.of(".", filter); 168 if (!Files.exists(p)) { 169 return; 170 } 171 } 172 throw new UserDataException("wildcards should be quoted, for example " + option + " \"Foo*\""); 173 } catch (InvalidPathException ipe) { 174 // ignore 175 } 176 } 177 acceptFilterOption(Deque<String> options, String expected)178 protected boolean acceptFilterOption(Deque<String> options, String expected) throws UserSyntaxException { 179 if (!acceptOption(options, expected)) { 180 return false; 181 } 182 if (options.isEmpty()) { 183 throw new UserSyntaxException("missing filter after " + expected); 184 } 185 String filter = options.peek(); 186 if (filter.startsWith("--")) { 187 throw new UserSyntaxException("missing filter after " + expected); 188 } 189 return true; 190 } 191 ensureMaxArgumentCount(Deque<String> options, int maxCount)192 final protected void ensureMaxArgumentCount(Deque<String> options, int maxCount) throws UserSyntaxException { 193 if (options.size() > maxCount) { 194 throw new UserSyntaxException("too many arguments"); 195 } 196 } 197 ensureMinArgumentCount(Deque<String> options, int minCount)198 final protected void ensureMinArgumentCount(Deque<String> options, int minCount) throws UserSyntaxException { 199 if (options.size() < minCount) { 200 throw new UserSyntaxException("too few arguments"); 201 } 202 } 203 getDirectory(String pathText)204 final protected Path getDirectory(String pathText) throws UserDataException { 205 try { 206 Path path = Paths.get(pathText).toAbsolutePath(); 207 if (!Files.exists((path))) { 208 throw new UserDataException("directory does not exist, " + pathText); 209 } 210 if (!Files.isDirectory(path)) { 211 throw new UserDataException("path must be directory, " + pathText); 212 } 213 return path; 214 } catch (InvalidPathException ipe) { 215 throw new UserDataException("invalid path '" + pathText + "'"); 216 } 217 } 218 getJFRInputFile(Deque<String> options)219 final protected Path getJFRInputFile(Deque<String> options) throws UserSyntaxException, UserDataException { 220 if (options.isEmpty()) { 221 throw new UserSyntaxException("missing file"); 222 } 223 String file = options.removeLast(); 224 if (file.startsWith("--")) { 225 throw new UserSyntaxException("missing file"); 226 } 227 try { 228 Path path = Paths.get(file).toAbsolutePath(); 229 ensureAccess(path); 230 ensureJFRFile(path); 231 return path; 232 } catch (IOError ioe) { 233 throw new UserDataException("i/o error reading file '" + file + "', " + ioe.getMessage()); 234 } catch (InvalidPathException ipe) { 235 throw new UserDataException("invalid path '" + file + "'"); 236 } 237 } 238 ensureAccess(Path path)239 private void ensureAccess(Path path) throws UserDataException { 240 try (RandomAccessFile rad = new RandomAccessFile(path.toFile(), "r")) { 241 if (rad.length() == 0) { 242 throw new UserDataException("file is empty '" + path + "'"); 243 } 244 rad.read(); // try to read 1 byte 245 } catch (FileNotFoundException e) { 246 throw new UserDataException("could not find file '" + path + "'"); 247 } catch (IOException e) { 248 throw new UserDataException("i/o error reading file '" + path + "', " + e.getMessage()); 249 } 250 } 251 couldNotReadError(Path p, IOException e)252 final protected void couldNotReadError(Path p, IOException e) throws UserDataException { 253 throw new UserDataException("could not read recording at " + p.toAbsolutePath() + ". " + e.getMessage()); 254 } 255 ensureFileDoesNotExist(Path file)256 final protected Path ensureFileDoesNotExist(Path file) throws UserDataException { 257 if (Files.exists(file)) { 258 throw new UserDataException("file '" + file + "' already exists"); 259 } 260 return file; 261 } 262 ensureJFRFile(Path path)263 final protected void ensureJFRFile(Path path) throws UserDataException { 264 if (!path.toString().endsWith(".jfr")) { 265 throw new UserDataException("filename must end with '.jfr'"); 266 } 267 } 268 displayUsage(PrintStream stream)269 protected void displayUsage(PrintStream stream) { 270 displayCommand(stream, this); 271 stream.println(); 272 displayOptionUsage(stream); 273 } 274 println()275 final protected void println() { 276 System.out.println(); 277 } 278 print(String text)279 final protected void print(String text) { 280 System.out.print(text); 281 } 282 println(String text)283 final protected void println(String text) { 284 System.out.println(text); 285 } 286 matches(String command)287 final protected boolean matches(String command) { 288 for (String s : getNames()) { 289 if (s.equals(command)) { 290 return true; 291 } 292 } 293 return false; 294 } 295 getAliases()296 protected List<String> getAliases() { 297 return Collections.emptyList(); 298 } 299 getNames()300 public List<String> getNames() { 301 List<String> names = new ArrayList<>(); 302 names.add(getName()); 303 names.addAll(getAliases()); 304 return names; 305 } 306 } 307