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;
18  
19  import static java.util.Objects.requireNonNull;
20  
21  import java.time.Instant;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.Iterator;
25  import java.util.LinkedHashMap;
26  import java.util.Map;
27  import java.util.concurrent.ConcurrentHashMap;
28  import java.util.concurrent.ConcurrentMap;
29  import java.util.function.Function;
30  import java.util.function.Supplier;
31  
32  import com.linecorp.centraldogma.common.Author;
33  import com.linecorp.centraldogma.common.CentralDogmaException;
34  import com.linecorp.centraldogma.common.RepositoryNotFoundException;
35  import com.linecorp.centraldogma.internal.Util;
36  import com.linecorp.centraldogma.server.storage.project.Project;
37  import com.linecorp.centraldogma.server.storage.repository.Repository;
38  import com.linecorp.centraldogma.server.storage.repository.RepositoryManager;
39  
40  public class RepositoryManagerWrapper implements RepositoryManager {
41      private final RepositoryManager delegate;
42      private final Function<Repository, Repository> repoWrapper;
43      private final ConcurrentMap<String, Repository> repos = new ConcurrentHashMap<>();
44  
45      public RepositoryManagerWrapper(RepositoryManager repoManager,
46                                      Function<Repository, Repository> repoWrapper) {
47  
48          delegate = requireNonNull(repoManager, "repoManager");
49          this.repoWrapper = requireNonNull(repoWrapper, "repoWrapper");
50          for (Map.Entry<String, Repository> entry : delegate.list().entrySet()) {
51              repos.computeIfAbsent(entry.getKey(), n -> repoWrapper.apply(entry.getValue()));
52          }
53      }
54  
55      @Override
56      public Project parent() {
57          return delegate.parent();
58      }
59  
60      @Override
61      public void close(Supplier<CentralDogmaException> failureCauseSupplier) {
62          delegate.close(failureCauseSupplier);
63      }
64  
65      @Override
66      public boolean exists(String name) {
67          return delegate.exists(name);
68      }
69  
70      @Override
71      public Repository get(String name) {
72          ensureOpen();
73          final Repository r = repos.get(name);
74          if (r == null) {
75              throw new RepositoryNotFoundException(name);
76          }
77          return r;
78      }
79  
80      @Override
81      public Repository create(String name, long creationTimeMillis, Author author) {
82          return repos.compute(
83                  name, (n, v) -> repoWrapper.apply(delegate.create(name, creationTimeMillis, author)));
84      }
85  
86      @Override
87      public Map<String, Repository> list() {
88          ensureOpen();
89          final int estimatedSize = repos.size();
90          final String[] names = repos.keySet().toArray(new String[estimatedSize]);
91          Arrays.sort(names);
92  
93          final Map<String, Repository> ret = new LinkedHashMap<>(names.length);
94          for (String name : names) {
95              final Repository repo = repos.get(name);
96              if (repo != null) {
97                  ret.put(name, repo);
98              }
99          }
100         return Collections.unmodifiableMap(ret);
101     }
102 
103     @Override
104     public Map<String, Instant> listRemoved() {
105         return delegate.listRemoved();
106     }
107 
108     @Override
109     public void remove(String name) {
110         repos.compute(name, (n, v) -> {
111             delegate.remove(n);
112             return null;
113         });
114     }
115 
116     @Override
117     public Repository unremove(String name) {
118         ensureOpen();
119         return repos.computeIfAbsent(name, n -> repoWrapper.apply(delegate.unremove(n)));
120     }
121 
122     @Override
123     public void purgeMarked() {
124         delegate.purgeMarked();
125     }
126 
127     @Override
128     public void markForPurge(String name) {
129         repos.compute(name, (n, v) -> {
130             delegate.markForPurge(name);
131             return null;
132         });
133     }
134 
135     @Override
136     public void ensureOpen() {
137         delegate.ensureOpen();
138     }
139 
140     @Override
141     public String toString() {
142         final StringBuilder sb = new StringBuilder(128);
143         sb.append(Util.simpleTypeName(getClass()) + '{');
144         sb.append("repositoryManager=");
145         sb.append(delegate);
146         sb.append(", repos={");
147         final Iterator<Map.Entry<String, Repository>> iterator = repos.entrySet().iterator();
148         Map.Entry<String, Repository> entry;
149         if (iterator.hasNext()) {
150             for (;;) {
151                 entry = iterator.next();
152                 sb.append(entry.getKey());
153                 sb.append('=');
154                 sb.append(Util.simpleTypeName(entry.getValue()));
155                 if (!iterator.hasNext()) {
156                     break;
157                 }
158                 sb.append(", ");
159             }
160         }
161         sb.append("}}");
162         return sb.toString();
163     }
164 }