| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
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 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 Exception { | 
 | 
        // Probe to see if repository can be created, needed for fail fast  | 
 | 
          | 
 | 
        this.repository = createRepository(baseLocation);  | 
 | 
        try { | 
 | 
            // Remove so we don't "leak" repositories, if JFR is never started  | 
 | 
              | 
 | 
            SecuritySupport.delete(repository);  | 
 | 
        } catch (IOException ioe) { | 
 | 
            Logger.log(LogTag.JFR, LogLevel.INFO, "Could not delete disk repository " + repository);  | 
 | 
        }  | 
 | 
        this.baseLocation = baseLocation;  | 
 | 
    }  | 
 | 
 | 
 | 
    synchronized void ensureRepository() throws Exception { | 
 | 
        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 Exception { | 
 | 
        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 Exception("Unable to create JFR repository directory using base location (" + basePath + ")"); | 
 | 
        }  | 
 | 
        SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f);  | 
 | 
        return canonicalRepositoryPath;  | 
 | 
    }  | 
 | 
 | 
 | 
    private static SafePath createRealBasePath(SafePath safePath) throws Exception { | 
 | 
        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.ERROR, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    public synchronized SafePath getRepositoryPath() { | 
 | 
        return repository;  | 
 | 
    }  | 
 | 
}  |