src/jdk.jfr/share/classes/jdk/jfr/internal/Repository.java
changeset 50113 caf115bb98ad
child 50745 a390cbb82d47
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     1 /*
       
     2  * Copyright (c) 2012, 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;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.nio.file.Path;
       
    30 import java.time.Instant;
       
    31 import java.time.LocalDateTime;
       
    32 import java.time.format.DateTimeFormatter;
       
    33 import java.util.HashSet;
       
    34 import java.util.Set;
       
    35 
       
    36 import jdk.jfr.internal.SecuritySupport.SafePath;
       
    37 
       
    38 public final class Repository {
       
    39 
       
    40     private static final int MAX_REPO_CREATION_RETRIES = 1000;
       
    41     private static final JVM jvm = JVM.getJVM();
       
    42     private static final Repository instance = new Repository();
       
    43 
       
    44     static DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter
       
    45             .ofPattern("yyyy_MM_dd_HH_mm_ss");
       
    46 
       
    47     private final Set<SafePath> cleanupDirectories = new HashSet<>();
       
    48     private SafePath baseLocation;
       
    49     private SafePath repository;
       
    50 
       
    51     private Repository() {
       
    52     }
       
    53 
       
    54     public static Repository getRepository() {
       
    55         return instance;
       
    56     }
       
    57 
       
    58     public synchronized void setBasePath(SafePath baseLocation) throws Exception {
       
    59         // Probe to see if repository can be created, needed for fail fast
       
    60         // during JVM startup or JFR.configure
       
    61         this.repository = createRepository(baseLocation);
       
    62         try {
       
    63             // Remove so we don't "leak" repositories, if JFR is never started
       
    64             // and shutdown hook not added.
       
    65             SecuritySupport.delete(repository);
       
    66         } catch (IOException ioe) {
       
    67             Logger.log(LogTag.JFR, LogLevel.INFO, "Could not delete disk repository " + repository);
       
    68         }
       
    69         this.baseLocation = baseLocation;
       
    70     }
       
    71 
       
    72     synchronized void ensureRepository() throws Exception {
       
    73         if (baseLocation == null) {
       
    74             setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
       
    75         }
       
    76     }
       
    77 
       
    78     synchronized RepositoryChunk newChunk(Instant timestamp) {
       
    79         try {
       
    80             if (!SecuritySupport.existDirectory(repository)) {
       
    81                 this.repository = createRepository(baseLocation);
       
    82                 jvm.setRepositoryLocation(repository.toString());
       
    83                 cleanupDirectories.add(repository);
       
    84             }
       
    85             return new RepositoryChunk(repository, timestamp);
       
    86         } catch (Exception e) {
       
    87             String errorMsg = String.format("Could not create chunk in repository %s, %s", repository, e.getMessage());
       
    88             Logger.log(LogTag.JFR, LogLevel.ERROR, errorMsg);
       
    89             jvm.abort(errorMsg);
       
    90             throw new InternalError("Could not abort after JFR disk creation error");
       
    91         }
       
    92     }
       
    93 
       
    94     private static SafePath createRepository(SafePath basePath) throws Exception {
       
    95         SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
       
    96         SafePath f = null;
       
    97 
       
    98         String basename = REPO_DATE_FORMAT.format(LocalDateTime.now()) + "_" + JVM.getJVM().getPid();
       
    99         String name = basename;
       
   100 
       
   101         int i = 0;
       
   102         for (; i < MAX_REPO_CREATION_RETRIES; i++) {
       
   103             f = new SafePath(canonicalBaseRepositoryPath.toPath().resolve(name));
       
   104             if (tryToUseAsRepository(f)) {
       
   105                 break;
       
   106             }
       
   107             name = basename + "_" + i;
       
   108         }
       
   109 
       
   110         if (i == MAX_REPO_CREATION_RETRIES) {
       
   111             throw new Exception("Unable to create JFR repository directory using base location (" + basePath + ")");
       
   112         }
       
   113         SafePath canonicalRepositoryPath = SecuritySupport.toRealPath(f);
       
   114         return canonicalRepositoryPath;
       
   115     }
       
   116 
       
   117     private static SafePath createRealBasePath(SafePath safePath) throws Exception {
       
   118         if (SecuritySupport.exists(safePath)) {
       
   119             if (!SecuritySupport.isWritable(safePath)) {
       
   120                 throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
       
   121             }
       
   122             return SecuritySupport.toRealPath(safePath);
       
   123         }
       
   124         SafePath p = SecuritySupport.createDirectories(safePath);
       
   125         return SecuritySupport.toRealPath(p);
       
   126     }
       
   127 
       
   128     private static boolean tryToUseAsRepository(final SafePath path) {
       
   129         Path parent = path.toPath().getParent();
       
   130         if (parent == null) {
       
   131             return false;
       
   132         }
       
   133         try {
       
   134             try {
       
   135                 SecuritySupport.createDirectories(path);
       
   136             } catch (Exception e) {
       
   137                 // file already existed or some other problem occurred
       
   138             }
       
   139             if (!SecuritySupport.exists(path)) {
       
   140                 return false;
       
   141             }
       
   142             if (!SecuritySupport.isDirectory(path)) {
       
   143                 return false;
       
   144             }
       
   145             return true;
       
   146         } catch (IOException io) {
       
   147             return false;
       
   148         }
       
   149     }
       
   150 
       
   151     synchronized void clear() {
       
   152         for (SafePath p : cleanupDirectories) {
       
   153             try {
       
   154                 SecuritySupport.clearDirectory(p);
       
   155                 Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
       
   156             } catch (IOException e) {
       
   157                 Logger.log(LogTag.JFR, LogLevel.ERROR, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
       
   158             }
       
   159         }
       
   160     }
       
   161 
       
   162     public synchronized SafePath getRepositoryPath() {
       
   163         return repository;
       
   164     }
       
   165 }