| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package jdk.jfr.internal.tool;  | 
 | 
 | 
 | 
import java.io.FileOutputStream;  | 
 | 
import java.io.IOException;  | 
 | 
import java.io.PrintStream;  | 
 | 
import java.nio.channels.FileChannel;  | 
 | 
import java.nio.file.DirectoryStream;  | 
 | 
import java.nio.file.Files;  | 
 | 
import java.nio.file.Path;  | 
 | 
import java.nio.file.Paths;  | 
 | 
import java.util.ArrayList;  | 
 | 
import java.util.Collections;  | 
 | 
import java.util.Deque;  | 
 | 
import java.util.List;  | 
 | 
 | 
 | 
final class Assemble extends Command { | 
 | 
 | 
 | 
    @Override  | 
 | 
    public String getName() { | 
 | 
        return "assemble";  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public List<String> getOptionSyntax() { | 
 | 
        return Collections.singletonList("<repository> <file>"); | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public String getDescription() { | 
 | 
        return "Assemble leftover chunks from a disk repository into a recording file";  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public void displayOptionUsage(PrintStream stream) { | 
 | 
        stream.println("  <repository>   Directory where the repository is located"); | 
 | 
        stream.println();  | 
 | 
        stream.println("  <file>         Name of the recording file (.jfr) to create"); | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public void execute(Deque<String> options) throws UserSyntaxException, UserDataException { | 
 | 
        ensureMinArgumentCount(options, 2);  | 
 | 
        ensureMaxArgumentCount(options, 2);  | 
 | 
        Path repository = getDirectory(options.pop());  | 
 | 
 | 
 | 
        Path file = Paths.get(options.pop());  | 
 | 
        ensureFileDoesNotExist(file);  | 
 | 
        ensureJFRFile(file);  | 
 | 
 | 
 | 
        try (FileOutputStream fos = new FileOutputStream(file.toFile())) { | 
 | 
            List<Path> files = listJFRFiles(repository);  | 
 | 
            if (files.isEmpty()) { | 
 | 
                throw new UserDataException("no *.jfr files found at " + repository); | 
 | 
            }  | 
 | 
            println();  | 
 | 
            println("Assembling files... "); | 
 | 
            println();  | 
 | 
            transferTo(files, file, fos.getChannel());  | 
 | 
            println();  | 
 | 
            println("Finished."); | 
 | 
        } catch (IOException e) { | 
 | 
            throw new UserDataException("could not open destination file " + file + ". " + e.getMessage()); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private List<Path> listJFRFiles(Path path) throws UserDataException { | 
 | 
        try { | 
 | 
            List<Path> files = new ArrayList<>();  | 
 | 
            if (Files.isDirectory(path)) { | 
 | 
                try (DirectoryStream<Path> stream = Files.newDirectoryStream(path, "*.jfr")) { | 
 | 
                    for (Path p : stream) { | 
 | 
                        if (!Files.isDirectory(p) && Files.isReadable(p)) { | 
 | 
                            files.add(p);  | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            files.sort((u, v) -> u.getFileName().compareTo(v.getFileName()));  | 
 | 
            return files;  | 
 | 
        } catch (IOException ioe) { | 
 | 
            throw new UserDataException("could not list *.jfr for directory " + path + ". " + ioe.getMessage()); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    private void transferTo(List<Path> sourceFiles, Path output, FileChannel out) throws UserDataException { | 
 | 
        long pos = 0;  | 
 | 
        for (Path p : sourceFiles) { | 
 | 
            println(" " + p.toString()); | 
 | 
            try (FileChannel sourceChannel = FileChannel.open(p)) { | 
 | 
                long rem = Files.size(p);  | 
 | 
                while (rem > 0) { | 
 | 
                    long n = Math.min(rem, 1024 * 1024);  | 
 | 
                    long w = out.transferFrom(sourceChannel, pos, n);  | 
 | 
                    pos += w;  | 
 | 
                    rem -= w;  | 
 | 
                }  | 
 | 
            } catch (IOException ioe) { | 
 | 
                throw new UserDataException("could not copy recording chunk " + p + " to new file. " + ioe.getMessage()); | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |