blob: 87c015be26c71b2899c02741b5899b362879f5f4 [file] [log] [blame]
/*
* Copyright (c) 2012, 2019, 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.nio.file.Path;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.Set;
import jdk.jfr.internal.SecuritySupport.SafePath;
public final class Repository {
private static final int MAX_REPO_CREATION_RETRIES = 1000;
private static final JVM jvm = JVM.getJVM();
private static final Repository instance = new Repository();
public final static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter
.ofPattern("yyyy_MM_dd_HH_mm_ss");
private static final String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
private final Set<SafePath> cleanupDirectories = new HashSet<>();
private SafePath baseLocation;
private SafePath repository;
private Repository() {
}
public static Repository getRepository() {
return instance;
}
public synchronized void setBasePath(SafePath baseLocation) throws IOException {
if(baseLocation.equals(this.baseLocation)) {
Logger.log(LogTag.JFR, LogLevel.INFO, "Same base repository path " + baseLocation.toString() + " is set");
return;
}
// Probe to see if repository can be created, needed for fail fast
// during JVM startup or JFR.configure
this.repository = createRepository(baseLocation);
try {
// Remove so we don't "leak" repositories, if JFR is never started
// and shutdown hook not added.
SecuritySupport.delete(repository);
} catch (IOException ioe) {
Logger.log(LogTag.JFR, LogLevel.INFO, "Could not delete disk repository " + repository);
}
this.baseLocation = baseLocation;
}
public synchronized void ensureRepository() throws IOException {
if (baseLocation == null) {
setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
}
}
synchronized RepositoryChunk newChunk(Instant timestamp) {
try {
if (!SecuritySupport.existDirectory(repository)) {
this.repository = createRepository(baseLocation);
jvm.setRepositoryLocation(repository.toString());
cleanupDirectories.add(repository);
}
return new RepositoryChunk(repository, timestamp);
} catch (Exception e) {
String errorMsg = String.format("Could not create chunk in repository %s, %s", repository, e.getMessage());
Logger.log(LogTag.JFR, LogLevel.ERROR, errorMsg);
jvm.abort(errorMsg);
throw new InternalError("Could not abort after JFR disk creation error");
}
}
private static SafePath createRepository(SafePath basePath) throws IOException {
SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
SafePath f = null;
String basename = REPO_DATE_FORMAT.format(LocalDateTime.now()) + "_" + JVM.getJVM().getPid();
String name = basename;
int i = 0;
for (; i < MAX_REPO_CREATION_RETRIES; i++) {
f = new SafePath(canonicalBaseRepositoryPath.toPath().resolve(name));
if (tryToUseAsRepository(f)) {
break;
}
name = basename + "_" + i;
}
if (i == MAX_REPO_CREATION_RETRIES) {
throw new IOException("Unable to create JFR repository directory using base location (" + basePath + ")");
}
SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f);
SecuritySupport.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, canonicalRepositoryPath.toString());
return canonicalRepositoryPath;
}
private static SafePath createRealBasePath(SafePath safePath) throws IOException {
if (SecuritySupport.exists(safePath)) {
if (!SecuritySupport.isWritable(safePath)) {
throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
}
return SecuritySupport.toRealPath(safePath);
}
SafePath p = SecuritySupport.createDirectories(safePath);
return SecuritySupport.toRealPath(p);
}
private static boolean tryToUseAsRepository(final SafePath path) {
Path parent = path.toPath().getParent();
if (parent == null) {
return false;
}
try {
try {
SecuritySupport.createDirectories(path);
} catch (Exception e) {
// file already existed or some other problem occurred
}
if (!SecuritySupport.exists(path)) {
return false;
}
if (!SecuritySupport.isDirectory(path)) {
return false;
}
return true;
} catch (IOException io) {
return false;
}
}
synchronized void clear() {
for (SafePath p : cleanupDirectories) {
try {
SecuritySupport.clearDirectory(p);
Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
} catch (IOException e) {
Logger.log(LogTag.JFR, LogLevel.INFO, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
}
}
}
public synchronized SafePath getRepositoryPath() {
return repository;
}
}