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.cmd; 27 28 import java.io.FileOutputStream; 29 import java.io.IOException; 30 import java.nio.channels.FileChannel; 31 import java.nio.file.DirectoryStream; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.nio.file.Paths; 35 import java.util.ArrayList; 36 import java.util.Deque; 37 import java.util.List; 38 39 final class ReconstructCommand extends Command { 40 41 @Override 42 public String getOptionSyntax() { 43 return "<repository> <file>"; 44 } 45 46 @Override 47 public String getName() { 48 return "reconstruct"; 49 } 50 51 @Override 52 public String getDescription() { 53 return "Assemble leftover chunks, from a disk repository, into a recording file (.jfr)"; 54 } 55 56 @Override 57 public void displayOptionUsage() { 58 println(" <repository> Directory where the repository is located"); 59 println(); 60 println(" <file> Name of the recording file (.jfr) to create"); 61 } 62 63 @Override 64 public void execute(Deque<String> options) { 65 ensureMinArgumentCount(options, 2); 66 ensureMaxArgumentCount(options, 2); 67 68 Path repository = Paths.get(options.pop()).toAbsolutePath(); 69 if (!Files.exists(repository)) { 70 userFailed("Could not find disk repository at " + repository); 71 } 72 if (!Files.isDirectory(repository)) { 73 userFailed("Must specify a directory as disk repository"); 74 } 75 Path output = Paths.get(options.pop()); 76 ensureFileDoesNotExist(output); 77 ensureJFRFile(output); 78 79 try (FileOutputStream fos = new FileOutputStream(output.toFile())) { 80 List<Path> files = listJFRFiles(repository); 81 if (files.isEmpty()) { 82 throw new IllegalStateException("No *.jfr files found at " + repository); 83 } 84 println(); 85 println("Combining files... "); 86 println(); 87 transferTo(files, output, fos.getChannel()); 88 println(); 89 println("Reconstruction complete."); 90 } catch (IOException e) { 91 userFailed("Could not open destination file " + output + ". " + e.getMessage()); 92 } 93 } 94 95 private List<Path> listJFRFiles(Path path) throws IOException { 96 try { 97 List<Path> files = new ArrayList<>(); 98 if (Files.isDirectory(path)) { 99 try (DirectoryStream<Path> stream = Files.newDirectoryStream(path, "*.jfr")) { 100 for (Path p : stream) { 101 if (!Files.isDirectory(p) && Files.isReadable(p)) { 102 files.add(p); 103 } 104 } 105 } 106 } 107 files.sort((u, v) -> u.getFileName().compareTo(v.getFileName())); 108 return files; 109 } catch (IOException ioe) { 110 throw new IllegalStateException("Could not list *.jfr for directory " + path + ". " + ioe.getMessage()); 111 } 112 } 113 114 private void transferTo(List<Path> sourceFiles, Path output, FileChannel out) { 115 long pos = 0; 116 for (Path p : sourceFiles) { 117 println(" " + p.toString()); 118 try (FileChannel sourceChannel = FileChannel.open(p)) { 119 long rem = Files.size(p); 120 while (rem > 0) { 121 long n = Math.min(rem, 1024 * 1024); 122 long w = out.transferFrom(sourceChannel, pos, n); 123 pos += w; 124 rem -= w; 125 } 126 } catch (IOException ioe) { 127 throw new IllegalStateException("Could not copy recording chunk " + p + " to new file. " + ioe.getMessage()); 128 } 129 } 130 } 131 }