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