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.repository.internal;
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.building.Source;
27  import org.apache.maven.model.building.ModelCache;
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) {
42          ConcurrentHashMap<Object, Supplier<?>> cache;
43          RepositoryCache repositoryCache = session.getCache();
44          if (repositoryCache == null) {
45              cache = new ConcurrentHashMap<>();
46          } else {
47              cache = (ConcurrentHashMap<Object, Supplier<?>>)
48                      repositoryCache.computeIfAbsent(session, KEY, ConcurrentHashMap::new);
49          }
50          return new DefaultModelCache(cache);
51      }
52  
53      private final ConcurrentMap<Object, Supplier<?>> cache;
54  
55      private DefaultModelCache(ConcurrentMap<Object, Supplier<?>> cache) {
56          this.cache = requireNonNull(cache);
57      }
58  
59      @Override
60      @SuppressWarnings({"unchecked"})
61      public <T> T computeIfAbsent(String groupId, String artifactId, String version, String tag, Supplier<T> data) {
62          return (T) computeIfAbsent(new GavCacheKey(groupId, artifactId, version, tag), data);
63      }
64  
65      @Override
66      @SuppressWarnings({"unchecked"})
67      public <T> T computeIfAbsent(Source path, String tag, Supplier<T> data) {
68          return (T) computeIfAbsent(new SourceCacheKey(path, tag), data);
69      }
70  
71      protected Object computeIfAbsent(Object key, Supplier<?> data) {
72          return cache.computeIfAbsent(key, k -> new CachingSupplier<>(data)).get();
73      }
74  
75      static class GavCacheKey {
76  
77          private final String gav;
78  
79          private final String tag;
80  
81          private final int hash;
82  
83          GavCacheKey(String groupId, String artifactId, String version, String tag) {
84              this(gav(groupId, artifactId, version), tag);
85          }
86  
87          GavCacheKey(String gav, String tag) {
88              this.gav = gav;
89              this.tag = tag;
90              this.hash = Objects.hash(gav, tag);
91          }
92  
93          private static String gav(String groupId, String artifactId, String version) {
94              StringBuilder sb = new StringBuilder();
95              if (groupId != null) {
96                  sb.append(groupId);
97              }
98              sb.append(":");
99              if (artifactId != null) {
100                 sb.append(artifactId);
101             }
102             sb.append(":");
103             if (version != null) {
104                 sb.append(version);
105             }
106             return sb.toString();
107         }
108 
109         @Override
110         public boolean equals(Object obj) {
111             if (this == obj) {
112                 return true;
113             }
114             if (null == obj || !getClass().equals(obj.getClass())) {
115                 return false;
116             }
117             GavCacheKey that = (GavCacheKey) obj;
118             return Objects.equals(this.gav, that.gav) && Objects.equals(this.tag, that.tag);
119         }
120 
121         @Override
122         public int hashCode() {
123             return hash;
124         }
125 
126         @Override
127         public String toString() {
128             return "GavCacheKey{" + "gav='" + gav + '\'' + ", tag='" + tag + '\'' + '}';
129         }
130     }
131 
132     private static final class SourceCacheKey {
133         private final Source source;
134 
135         private final String tag;
136 
137         private final int hash;
138 
139         SourceCacheKey(Source source, String tag) {
140             this.source = source;
141             this.tag = tag;
142             this.hash = Objects.hash(source, tag);
143         }
144 
145         @Override
146         public String toString() {
147             return "SourceCacheKey{" + "source=" + source + ", tag='" + tag + '\'' + '}';
148         }
149 
150         @Override
151         public boolean equals(Object obj) {
152             if (this == obj) {
153                 return true;
154             }
155             if (null == obj || !getClass().equals(obj.getClass())) {
156                 return false;
157             }
158             SourceCacheKey that = (SourceCacheKey) obj;
159             return Objects.equals(this.source, that.source) && Objects.equals(this.tag, that.tag);
160         }
161 
162         @Override
163         public int hashCode() {
164             return hash;
165         }
166     }
167 
168     static class CachingSupplier<T> implements Supplier<T> {
169         final Supplier<T> supplier;
170         volatile Object value;
171 
172         CachingSupplier(Supplier<T> supplier) {
173             this.supplier = supplier;
174         }
175 
176         @Override
177         @SuppressWarnings({"unchecked", "checkstyle:InnerAssignment"})
178         public T get() {
179             Object v;
180             if ((v = value) == null) {
181                 synchronized (this) {
182                     if ((v = value) == null) {
183                         try {
184                             v = value = supplier.get();
185                         } catch (Exception e) {
186                             v = value = new AltRes(e);
187                         }
188                     }
189                 }
190             }
191             if (v instanceof AltRes) {
192                 uncheckedThrow(((AltRes) v).t);
193             }
194             return (T) v;
195         }
196 
197         static class AltRes {
198             final Throwable t;
199 
200             AltRes(Throwable t) {
201                 this.t = t;
202             }
203         }
204     }
205 
206     @SuppressWarnings("unchecked")
207     static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
208         throw (T) t; // rely on vacuous cast
209     }
210 }