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