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.buildcache;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Provider;
24  
25  import java.util.LinkedHashSet;
26  import java.util.Set;
27  import java.util.concurrent.ConcurrentHashMap;
28  import java.util.concurrent.ConcurrentMap;
29  
30  import org.apache.maven.SessionScoped;
31  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
32  import org.apache.maven.buildcache.checksum.MavenProjectInput;
33  import org.apache.maven.buildcache.xml.CacheConfig;
34  import org.apache.maven.buildcache.xml.build.ProjectsInputInfo;
35  import org.apache.maven.execution.MavenSession;
36  import org.apache.maven.lifecycle.internal.builder.BuilderCommon;
37  import org.apache.maven.project.MavenProject;
38  import org.eclipse.aether.RepositorySystem;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  @SessionScoped
43  @Named
44  public class DefaultProjectInputCalculator implements ProjectInputCalculator {
45  
46      private static final Logger LOGGER = LoggerFactory.getLogger(DefaultProjectInputCalculator.class);
47  
48      private final Provider<MavenSession> providerSession;
49      private final RemoteCacheRepository remoteCache;
50      private final CacheConfig cacheConfig;
51      private final RepositorySystem repoSystem;
52      private final NormalizedModelProvider normalizedModelProvider;
53      private final MultiModuleSupport multiModuleSupport;
54      private final ArtifactHandlerManager artifactHandlerManager;
55  
56      private final ConcurrentMap<String, ProjectsInputInfo> checkSumMap = new ConcurrentHashMap<>();
57  
58      private static final ThreadLocal<Set<String>> CURRENTLY_CALCULATING = ThreadLocal.withInitial(LinkedHashSet::new);
59  
60      @Inject
61      public DefaultProjectInputCalculator(
62              Provider<MavenSession> providerSession,
63              RemoteCacheRepository remoteCache,
64              CacheConfig cacheConfig,
65              RepositorySystem repoSystem,
66              NormalizedModelProvider rawModelProvider,
67              MultiModuleSupport multiModuleSupport,
68              ArtifactHandlerManager artifactHandlerManager) {
69          this.providerSession = providerSession;
70          this.remoteCache = remoteCache;
71          this.cacheConfig = cacheConfig;
72          this.repoSystem = repoSystem;
73          this.normalizedModelProvider = rawModelProvider;
74          this.multiModuleSupport = multiModuleSupport;
75          this.artifactHandlerManager = artifactHandlerManager;
76      }
77  
78      @Override
79      public ProjectsInputInfo calculateInput(MavenProject project) {
80          LOGGER.info(
81                  "Going to calculate checksum for project [groupId={}, artifactId={}, version={}]",
82                  project.getGroupId(),
83                  project.getArtifactId(),
84                  project.getVersion());
85  
86          String key = BuilderCommon.getKey(project);
87          // NOTE: Do not use ConcurrentHashMap.computeIfAbsent() here because of recursive calls
88          // this could lead to runtime exception - IllegalStateException("Recursive update")
89          // in jdk 8 the result of attempt to modify items with the same hash code could lead to infinite loop
90          ProjectsInputInfo projectsInputInfo = checkSumMap.get(key);
91          if (projectsInputInfo != null) {
92              return projectsInputInfo;
93          }
94          projectsInputInfo = calculateInputInternal(key, project);
95          checkSumMap.put(key, projectsInputInfo);
96          return projectsInputInfo;
97      }
98  
99      private ProjectsInputInfo calculateInputInternal(String key, MavenProject project) {
100         Set<String> projectsSet = CURRENTLY_CALCULATING.get();
101 
102         if (!projectsSet.add(key)) {
103             throw new IllegalStateException("Checksum for project is already calculating. "
104                     + "Is there a cyclic dependencies? [project=" + key
105                     + ", setOfCalculatingProjects=" + projectsSet + "]");
106         }
107         try {
108             final MavenSession mavenSession = providerSession.get();
109             final MavenProjectInput input = new MavenProjectInput(
110                     project,
111                     normalizedModelProvider,
112                     multiModuleSupport,
113                     this,
114                     mavenSession,
115                     cacheConfig,
116                     repoSystem,
117                     remoteCache,
118                     artifactHandlerManager);
119             return input.calculateChecksum();
120         } catch (Exception e) {
121             throw new RuntimeException("Failed to calculate checksums for " + project.getArtifactId(), e);
122         } finally {
123             projectsSet.remove(key);
124         }
125     }
126 }