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.model.building;
20  
21  import java.nio.file.Files;
22  import java.nio.file.Path;
23  import java.util.ArrayList;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.concurrent.ConcurrentHashMap;
29  
30  import org.apache.maven.model.Model;
31  import org.apache.maven.model.building.DefaultTransformerContext.GAKey;
32  import org.apache.maven.model.building.DefaultTransformerContext.Holder;
33  
34  /**
35   * Builds up the transformer context.
36   * After the buildplan is ready, the build()-method returns the immutable context useful during distribution.
37   * This is an inner class, as it must be able to call readRawModel()
38   *
39   * @since 4.0.0
40   */
41  class DefaultTransformerContextBuilder implements TransformerContextBuilder {
42      private final Graph dag = new Graph();
43      private final DefaultModelBuilder defaultModelBuilder;
44      private final DefaultTransformerContext context;
45  
46      private final Map<String, Set<FileModelSource>> mappedSources = new ConcurrentHashMap<>(64);
47  
48      private volatile boolean fullReactorLoaded;
49  
50      DefaultTransformerContextBuilder(DefaultModelBuilder defaultModelBuilder) {
51          this.defaultModelBuilder = defaultModelBuilder;
52          this.context = new DefaultTransformerContext(defaultModelBuilder.getModelProcessor());
53      }
54  
55      /**
56       * If an interface could be extracted, DefaultModelProblemCollector should be ModelProblemCollectorExt
57       */
58      @Override
59      public TransformerContext initialize(ModelBuildingRequest request, ModelProblemCollector collector) {
60          // We must assume the TransformerContext was created using this.newTransformerContextBuilder()
61          DefaultModelProblemCollector problems = (DefaultModelProblemCollector) collector;
62          return new TransformerContext() {
63  
64              @Override
65              public Path locate(Path path) {
66                  return context.locate(path);
67              }
68  
69              @Override
70              public String getUserProperty(String key) {
71                  return context.userProperties.computeIfAbsent(
72                          key, k -> request.getUserProperties().getProperty(key));
73              }
74  
75              @Override
76              public Model getRawModel(Path from, String gId, String aId) {
77                  Model model = findRawModel(from, gId, aId);
78                  if (model != null) {
79                      context.modelByGA.put(new GAKey(gId, aId), new Holder(model));
80                      context.modelByPath.put(model.getPomPath(), new Holder(model));
81                  }
82                  return model;
83              }
84  
85              @Override
86              public Model getRawModel(Path from, Path path) {
87                  Model model = findRawModel(from, path);
88                  if (model != null) {
89                      String groupId = defaultModelBuilder.getGroupId(model);
90                      context.modelByGA.put(
91                              new DefaultTransformerContext.GAKey(groupId, model.getArtifactId()), new Holder(model));
92                      context.modelByPath.put(path, new Holder(model));
93                  }
94                  return model;
95              }
96  
97              private Model findRawModel(Path from, String groupId, String artifactId) {
98                  FileModelSource source = getSource(groupId, artifactId);
99                  if (source == null) {
100                     // we need to check the whole reactor in case it's a dependency
101                     loadFullReactor();
102                     source = getSource(groupId, artifactId);
103                 }
104                 if (source != null) {
105                     if (!addEdge(from, source.getPath(), problems)) {
106                         return null;
107                     }
108                     try {
109                         ModelBuildingRequest gaBuildingRequest =
110                                 new DefaultModelBuildingRequest(request).setModelSource(source);
111                         return defaultModelBuilder.readRawModel(gaBuildingRequest, problems);
112                     } catch (ModelBuildingException e) {
113                         // gathered with problem collector
114                     }
115                 }
116                 return null;
117             }
118 
119             private void loadFullReactor() {
120                 if (!fullReactorLoaded) {
121                     synchronized (DefaultTransformerContextBuilder.this) {
122                         if (!fullReactorLoaded) {
123                             doLoadFullReactor();
124                             fullReactorLoaded = true;
125                         }
126                     }
127                 }
128             }
129 
130             private void doLoadFullReactor() {
131                 Path rootDirectory = request.getRootDirectory();
132                 if (rootDirectory == null) {
133                     return;
134                 }
135                 List<Path> toLoad = new ArrayList<>();
136                 Path root = defaultModelBuilder.getModelProcessor().locateExistingPom(rootDirectory);
137                 toLoad.add(root);
138                 while (!toLoad.isEmpty()) {
139                     Path pom = toLoad.remove(0);
140                     try {
141                         ModelBuildingRequest gaBuildingRequest =
142                                 new DefaultModelBuildingRequest(request).setModelSource(new FileModelSource(pom));
143                         Model rawModel = defaultModelBuilder.readFileModel(gaBuildingRequest, problems);
144                         for (String module : rawModel.getModules()) {
145                             Path moduleFile = defaultModelBuilder
146                                     .getModelProcessor()
147                                     .locateExistingPom(pom.getParent().resolve(module));
148                             if (moduleFile != null) {
149                                 toLoad.add(moduleFile);
150                             }
151                         }
152                     } catch (ModelBuildingException e) {
153                         // gathered with problem collector
154                     }
155                 }
156             }
157 
158             private Model findRawModel(Path from, Path p) {
159                 if (!Files.isRegularFile(p)) {
160                     throw new IllegalArgumentException("Not a regular file: " + p);
161                 }
162 
163                 if (!addEdge(from, p, problems)) {
164                     return null;
165                 }
166 
167                 DefaultModelBuildingRequest req =
168                         new DefaultModelBuildingRequest(request).setPomPath(p).setModelSource(new FileModelSource(p));
169 
170                 try {
171                     return defaultModelBuilder.readRawModel(req, problems);
172                 } catch (ModelBuildingException e) {
173                     // gathered with problem collector
174                 }
175                 return null;
176             }
177         };
178     }
179 
180     private boolean addEdge(Path from, Path p, DefaultModelProblemCollector problems) {
181         try {
182             dag.addEdge(from.toString(), p.toString());
183             return true;
184         } catch (Graph.CycleDetectedException e) {
185             problems.add(new DefaultModelProblem(
186                     "Cycle detected between models at " + from + " and " + p,
187                     ModelProblem.Severity.FATAL,
188                     null,
189                     null,
190                     0,
191                     0,
192                     null,
193                     e));
194             return false;
195         }
196     }
197 
198     @Override
199     public TransformerContext build() {
200         return context;
201     }
202 
203     public FileModelSource getSource(String groupId, String artifactId) {
204         Set<FileModelSource> sources = mappedSources.get(groupId + ":" + artifactId);
205         if (sources == null) {
206             return null;
207         }
208         return sources.stream()
209                 .reduce((a, b) -> {
210                     throw new IllegalStateException(String.format(
211                             "No unique Source for %s:%s: %s and %s",
212                             groupId, artifactId, a.getLocation(), b.getLocation()));
213                 })
214                 .orElse(null);
215     }
216 
217     public void putSource(String groupId, String artifactId, FileModelSource source) {
218         mappedSources
219                 .computeIfAbsent(groupId + ":" + artifactId, k -> new HashSet<>())
220                 .add(source);
221     }
222 }