--- /dev/null 2017-11-09 09:38:01.297999907 +0100 +++ new/src/jdk.jfr/share/classes/jdk/jfr/internal/RepositoryChunk.java 2018-04-09 16:03:47.430000926 +0200 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.jfr.internal; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.Path; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.Comparator; +import java.util.Objects; + +import jdk.jfr.internal.SecuritySupport.SafePath; + +final class RepositoryChunk { + private static final int MAX_CHUNK_NAMES = 100; + + static final Comparator END_TIME_COMPARATOR = new Comparator() { + @Override + public int compare(RepositoryChunk c1, RepositoryChunk c2) { + return c1.endTime.compareTo(c2.endTime); + } + }; + + private final SafePath repositoryPath; + private final SafePath unFinishedFile; + private final SafePath file; + private final Instant startTime; + private final RandomAccessFile unFinishedRAF; + + private Instant endTime = null; // unfinished + private int refCount = 0; + private long size; + + RepositoryChunk(SafePath path, Instant startTime) throws Exception { + ZonedDateTime z = ZonedDateTime.now(); + String fileName = Repository.REPO_DATE_FORMAT.format( + LocalDateTime.ofInstant(startTime, z.getZone())); + this.startTime = startTime; + this.repositoryPath = path; + this.unFinishedFile = findFileName(repositoryPath, fileName, ".part"); + this.file = findFileName(repositoryPath, fileName, ".jfr"); + this.unFinishedRAF = SecuritySupport.createRandomAccessFile(unFinishedFile); + SecuritySupport.touch(file); + } + + private static SafePath findFileName(SafePath directory, String name, String extension) throws Exception { + Path p = directory.toPath().resolve(name + extension); + for (int i = 1; i < MAX_CHUNK_NAMES; i++) { + SafePath s = new SafePath(p); + if (!SecuritySupport.exists(s)) { + return s; + } + String extendedName = String.format("%s_%02d%s", name, i, extension); + p = directory.toPath().resolve(extendedName); + } + p = directory.toPath().resolve(name + "_" + System.currentTimeMillis() + extension); + return SecuritySupport.toRealPath(new SafePath(p)); + } + + public SafePath getUnfishedFile() { + return unFinishedFile; + } + + void finish(Instant endTime) { + try { + finishWithException(endTime); + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not finish chunk. " + e.getMessage()); + } + } + + private void finishWithException(Instant endTime) throws IOException { + unFinishedRAF.close(); + this.size = finish(unFinishedFile, file); + this.endTime = endTime; + Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Chunk finished: " + file); + } + + private static long finish(SafePath unFinishedFile, SafePath file) throws IOException { + Objects.requireNonNull(unFinishedFile); + Objects.requireNonNull(file); + SecuritySupport.delete(file); + SecuritySupport.moveReplace(unFinishedFile, file); + return SecuritySupport.getFileSize(file); + } + + public Instant getStartTime() { + return startTime; + } + + public Instant getEndTime() { + return endTime; + } + + private void delete(SafePath f) { + try { + SecuritySupport.delete(f); + Logger.log(LogTag.JFR, LogLevel.DEBUG, () -> "Repository chunk " + f + " deleted"); + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Repository chunk " + f + " could not be deleted: " + e.getMessage()); + if (f != null) { + SecuritySupport.deleteOnExit(f); + } + } + } + + private void destroy() { + if (!isFinished()) { + finish(Instant.MIN); + } + if (file != null) { + delete(file); + } + try { + unFinishedRAF.close(); + } catch (IOException e) { + Logger.log(LogTag.JFR, LogLevel.ERROR, () -> "Could not close random access file: " + unFinishedFile.toString() + ". File will not be deleted due to: " + e.getMessage()); + } + } + + public synchronized void use() { + ++refCount; + Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Use chunk " + toString() + " ref count now " + refCount); + } + + public synchronized void release() { + --refCount; + Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Release chunk " + toString() + " ref count now " + refCount); + if (refCount == 0) { + destroy(); + } + } + + @Override + @SuppressWarnings("deprecation") + protected void finalize() { + boolean destroy = false; + synchronized (this) { + if (refCount > 0) { + destroy = true; + } + } + if (destroy) { + destroy(); + } + } + + public long getSize() { + return size; + } + + public boolean isFinished() { + return endTime != null; + } + + @Override + public String toString() { + if (isFinished()) { + return file.toString(); + } + return unFinishedFile.toString(); + } + + ReadableByteChannel newChannel() throws IOException { + if (!isFinished()) { + throw new IOException("Chunk not finished"); + } + return ((SecuritySupport.newFileChannelToRead(file))); + } + + public boolean inInterval(Instant startTime, Instant endTime) { + if (startTime != null && getEndTime().isBefore(startTime)) { + return false; + } + if (endTime != null && getStartTime().isAfter(endTime)) { + return false; + } + return true; + } + + public SafePath getFile() { + return file; + } +}