1   /*
2    * Copyright 2017 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package com.linecorp.centraldogma.server.internal.storage.repository.git;
18  
19  import static java.util.Objects.requireNonNull;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.concurrent.Executor;
24  import java.util.concurrent.TimeUnit;
25  import java.util.function.BiConsumer;
26  import java.util.function.Supplier;
27  
28  import javax.annotation.Nullable;
29  
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  import com.linecorp.armeria.common.util.TextFormatter;
34  import com.linecorp.centraldogma.common.Author;
35  import com.linecorp.centraldogma.common.CentralDogmaException;
36  import com.linecorp.centraldogma.common.RepositoryExistsException;
37  import com.linecorp.centraldogma.common.RepositoryNotFoundException;
38  import com.linecorp.centraldogma.internal.Util;
39  import com.linecorp.centraldogma.server.internal.storage.DirectoryBasedStorageManager;
40  import com.linecorp.centraldogma.server.internal.storage.repository.RepositoryCache;
41  import com.linecorp.centraldogma.server.storage.project.Project;
42  import com.linecorp.centraldogma.server.storage.repository.Repository;
43  import com.linecorp.centraldogma.server.storage.repository.RepositoryManager;
44  
45  public class GitRepositoryManager extends DirectoryBasedStorageManager<Repository>
46          implements RepositoryManager {
47  
48      private static final Logger logger = LoggerFactory.getLogger(GitRepositoryManager.class);
49  
50      private final Project parent;
51      private final Executor repositoryWorker;
52  
53      @Nullable
54      private final RepositoryCache cache;
55  
56      public GitRepositoryManager(Project parent, File rootDir, Executor repositoryWorker,
57                                  Executor purgeWorker, @Nullable RepositoryCache cache) {
58          super(rootDir, Repository.class, purgeWorker);
59          this.parent = requireNonNull(parent, "parent");
60          this.repositoryWorker = requireNonNull(repositoryWorker, "repositoryWorker");
61          this.cache = cache;
62          init();
63      }
64  
65      @Override
66      public Project parent() {
67          return parent;
68      }
69  
70      @Override
71      protected Repository openChild(File childDir) throws Exception {
72          return new GitRepository(parent, childDir, repositoryWorker, cache);
73      }
74  
75      private static void deleteCruft(File dir) throws IOException {
76          logger.info("Deleting the cruft from previous migration: {}", dir);
77          Util.deleteFileTree(dir);
78          logger.info("Deleted the cruft from previous migration: {}", dir);
79      }
80  
81      @Override
82      protected Repository createChild(File childDir, Author author, long creationTimeMillis) throws Exception {
83          return new GitRepository(parent, childDir, repositoryWorker,
84                                   creationTimeMillis, author, cache);
85      }
86  
87      @Override
88      protected void closeChild(File childDir, Repository child,
89                                Supplier<CentralDogmaException> failureCauseSupplier) {
90          ((GitRepository) child).close(failureCauseSupplier);
91      }
92  
93      @Override
94      protected CentralDogmaException newStorageExistsException(String name) {
95          return new RepositoryExistsException(parent().name() + '/' + name);
96      }
97  
98      @Override
99      protected CentralDogmaException newStorageNotFoundException(String name) {
100         return new RepositoryNotFoundException(parent().name() + '/' + name);
101     }
102 
103     /**
104      * Logs the migration progress periodically.
105      */
106     private static class MigrationProgressLogger implements BiConsumer<Integer, Integer> {
107 
108         private static final long REPORT_INTERVAL_NANOS = TimeUnit.SECONDS.toNanos(10);
109 
110         private final String name;
111         private final long startTimeNanos;
112         private long lastReportTimeNanos;
113 
114         MigrationProgressLogger(Repository repo) {
115             name = repo.parent().name() + '/' + repo.name();
116             startTimeNanos = lastReportTimeNanos = System.nanoTime();
117         }
118 
119         @Override
120         public void accept(Integer current, Integer total) {
121             final long currentTimeNanos = System.nanoTime();
122             final long elapsedTimeNanos = currentTimeNanos - startTimeNanos;
123             if (currentTimeNanos - lastReportTimeNanos > REPORT_INTERVAL_NANOS) {
124                 logger.info("{}: {}% ({}/{}) - took {}",
125                             name, (int) ((double) current / total * 100),
126                             current, total, TextFormatter.elapsed(elapsedTimeNanos));
127                 lastReportTimeNanos = currentTimeNanos;
128             } else if (current.equals(total)) {
129                 logger.info("{}: 100% ({}/{}) - took {}",
130                             name, current, total,
131                             TextFormatter.elapsed(elapsedTimeNanos));
132             }
133         }
134     }
135 }