View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.impl.resolver;
20  
21  import java.util.Objects;
22  import java.util.concurrent.ConcurrentHashMap;
23  import java.util.concurrent.ConcurrentMap;
24  import java.util.function.Supplier;
25  
26  import org.apache.maven.api.services.ModelCache;
27  import org.apache.maven.api.services.Source;
28  import org.eclipse.aether.RepositoryCache;
29  import org.eclipse.aether.RepositorySystemSession;
30  
31  import static java.util.Objects.requireNonNull;
32  
33  /**
34   * A model builder cache backed by the repository system cache.
35   *
36   */
37  public class DefaultModelCache implements ModelCache {
38      private static final String KEY = DefaultModelCache.class.getName();
39  
40      @SuppressWarnings("unchecked")
41      public static ModelCache newInstance(RepositorySystemSession session, boolean anew) {
42          ConcurrentHashMap<Object, Supplier<?>> cache;
43          RepositoryCache repositoryCache = session != null ? session.getCache() : null;
44          if (repositoryCache == null) {
45              return new DefaultModelCache(new ConcurrentHashMap<>());
46          } else {
47              if (anew) {
48                  cache = new ConcurrentHashMap<>();
49                  repositoryCache.put(session, KEY, cache);
50              } else {
51                  cache = (ConcurrentHashMap<Object, Supplier<?>>)
52                          repositoryCache.computeIfAbsent(session, KEY, ConcurrentHashMap::new);
53              }
54              return new DefaultModelCache(cache);
55          }
56      }
57  
58      private final ConcurrentMap<Object, Supplier<?>> cache;
59  
60      public DefaultModelCache() {
61          this(new ConcurrentHashMap<>());
62      }
63  
64      private DefaultModelCache(ConcurrentMap<Object, Supplier<?>> cache) {
65          this.cache = requireNonNull(cache);
66      }
67  
68      @Override
69      @SuppressWarnings({"unchecked"})
70      public <T> T computeIfAbsent(String groupId, String artifactId, String version, String tag, Supplier<T> data) {
71          return (T) computeIfAbsent(new GavCacheKey(groupId, artifactId, version, tag), data);
72      }
73  
74      @Override
75      @SuppressWarnings({"unchecked"})
76      public <T> T computeIfAbsent(Source path, String tag, Supplier<T> data) {
77          return (T) computeIfAbsent(new SourceCacheKey(path, tag), data);
78      }
79  
80      protected Object computeIfAbsent(Object key, Supplier<?> data) {
81          return cache.computeIfAbsent(key, k -> new CachingSupplier<>(data)).get();
82      }
83  
84      static class GavCacheKey {
85  
86          private final String gav;
87  
88          private final String tag;
89  
90          private final int hash;
91  
92          GavCacheKey(String groupId, String artifactId, String version, String tag) {
93              this(gav(groupId, artifactId, version), tag);
94          }
95  
96          GavCacheKey(String gav, String tag) {
97              this.gav = gav;
98              this.tag = tag;
99              this.hash = Objects.hash(gav, tag);
100         }
101 
102         private static String gav(String groupId, String artifactId, String version) {
103             StringBuilder sb = new StringBuilder();
104             if (groupId != null) {
105                 sb.append(groupId);
106             }
107             sb.append(":");
108             if (artifactId != null) {
109                 sb.append(artifactId);
110             }
111             sb.append(":");
112             if (version != null) {
113                 sb.append(version);
114             }
115             return sb.toString();
116         }
117 
118         @Override
119         public boolean equals(Object obj) {
120             if (this == obj) {
121                 return true;
122             }
123             if (null == obj || !getClass().equals(obj.getClass())) {
124                 return false;
125             }
126             GavCacheKey that = (GavCacheKey) obj;
127             return Objects.equals(this.gav, that.gav) && Objects.equals(this.tag, that.tag);
128         }
129 
130         @Override
131         public int hashCode() {
132             return hash;
133         }
134 
135         @Override
136         public String toString() {
137             return "GavCacheKey[" + "gav='" + gav + '\'' + ", tag='" + tag + '\'' + ']';
138         }
139     }
140 
141     private static final class SourceCacheKey {
142         private final Source source;
143 
144         private final String tag;
145 
146         private final int hash;
147 
148         SourceCacheKey(Source source, String tag) {
149             this.source = source;
150             this.tag = tag;
151             this.hash = Objects.hash(source, tag);
152         }
153 
154         @Override
155         public String toString() {
156             return "SourceCacheKey[" + "location=" + source.getLocation() + ", tag=" + tag + ", path="
157                     + source.getPath() + ']';
158         }
159 
160         @Override
161         public boolean equals(Object obj) {
162             if (this == obj) {
163                 return true;
164             }
165             if (null == obj || !getClass().equals(obj.getClass())) {
166                 return false;
167             }
168             SourceCacheKey that = (SourceCacheKey) obj;
169             return Objects.equals(this.source, that.source) && Objects.equals(this.tag, that.tag);
170         }
171 
172         @Override
173         public int hashCode() {
174             return hash;
175         }
176     }
177 
178     static class CachingSupplier<T> implements Supplier<T> {
179         final Supplier<T> supplier;
180         volatile Object value;
181 
182         CachingSupplier(Supplier<T> supplier) {
183             this.supplier = supplier;
184         }
185 
186         @Override
187         @SuppressWarnings({"unchecked", "checkstyle:InnerAssignment"})
188         public T get() {
189             Object v;
190             if ((v = value) == null) {
191                 synchronized (this) {
192                     if ((v = value) == null) {
193                         try {
194                             v = value = supplier.get();
195                         } catch (Exception e) {
196                             v = value = new AltRes(e);
197                         }
198                     }
199                 }
200             }
201             if (v instanceof AltRes) {
202                 uncheckedThrow(((AltRes) v).t);
203             }
204             return (T) v;
205         }
206 
207         static class AltRes {
208             final Throwable t;
209 
210             AltRes(Throwable t) {
211                 this.t = t;
212             }
213         }
214     }
215 
216     @SuppressWarnings("unchecked")
217     static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
218         throw (T) t; // rely on vacuous cast
219     }
220 }