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.transformation.impl;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Provider;
24  
25  import java.nio.file.Path;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.stream.Collectors;
31  
32  import org.apache.maven.api.SessionData;
33  import org.apache.maven.api.model.Dependency;
34  import org.apache.maven.api.model.DependencyManagement;
35  import org.apache.maven.api.model.DistributionManagement;
36  import org.apache.maven.api.model.Model;
37  import org.apache.maven.api.model.ModelBase;
38  import org.apache.maven.api.model.Profile;
39  import org.apache.maven.api.model.Repository;
40  import org.apache.maven.api.services.ModelBuilderException;
41  import org.apache.maven.api.services.ModelBuilderRequest;
42  import org.apache.maven.api.services.ModelBuilderResult;
43  import org.apache.maven.api.services.ModelProblemCollector;
44  import org.apache.maven.api.services.ModelResolver;
45  import org.apache.maven.api.services.ModelSource;
46  import org.apache.maven.api.services.ModelTransformer;
47  import org.apache.maven.api.services.SuperPomProvider;
48  import org.apache.maven.api.services.model.DependencyManagementImporter;
49  import org.apache.maven.api.services.model.DependencyManagementInjector;
50  import org.apache.maven.api.services.model.InheritanceAssembler;
51  import org.apache.maven.api.services.model.LifecycleBindingsInjector;
52  import org.apache.maven.api.services.model.ModelInterpolator;
53  import org.apache.maven.api.services.model.ModelNormalizer;
54  import org.apache.maven.api.services.model.ModelPathTranslator;
55  import org.apache.maven.api.services.model.ModelProcessor;
56  import org.apache.maven.api.services.model.ModelUrlNormalizer;
57  import org.apache.maven.api.services.model.ModelValidator;
58  import org.apache.maven.api.services.model.ModelVersionParser;
59  import org.apache.maven.api.services.model.PluginConfigurationExpander;
60  import org.apache.maven.api.services.model.PluginManagementInjector;
61  import org.apache.maven.api.services.model.ProfileActivationContext;
62  import org.apache.maven.api.services.model.ProfileInjector;
63  import org.apache.maven.api.services.model.ProfileSelector;
64  import org.apache.maven.internal.impl.InternalSession;
65  import org.apache.maven.internal.impl.model.DefaultModelBuilder;
66  import org.apache.maven.internal.impl.model.DefaultProfileSelector;
67  import org.apache.maven.internal.impl.model.ProfileActivationFilePathInterpolator;
68  import org.apache.maven.internal.impl.resolver.DefaultModelCache;
69  import org.apache.maven.model.v4.MavenModelVersion;
70  import org.apache.maven.project.MavenProject;
71  import org.eclipse.aether.RepositorySystem;
72  import org.eclipse.aether.RepositorySystemSession;
73  import org.eclipse.aether.impl.RemoteRepositoryManager;
74  import org.slf4j.Logger;
75  import org.slf4j.LoggerFactory;
76  
77  @Named
78  class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
79      private static final String BOM_PACKAGING = "bom";
80  
81      public static final String POM_PACKAGING = "pom";
82  
83      @Inject
84      private ProfileInjector profileInjector;
85  
86      @Inject
87      private InheritanceAssembler inheritanceAssembler;
88  
89      @Inject
90      private DependencyManagementImporter dependencyManagementImporter;
91  
92      @Inject
93      private DependencyManagementInjector dependencyManagementInjector;
94  
95      @Inject
96      private LifecycleBindingsInjector lifecycleBindingsInjector;
97  
98      @Inject
99      private ModelInterpolator modelInterpolator;
100 
101     @Inject
102     private ModelNormalizer modelNormalizer;
103 
104     @Inject
105     private ModelPathTranslator modelPathTranslator;
106 
107     @Inject
108     private ModelProcessor modelProcessor;
109 
110     @Inject
111     private ModelUrlNormalizer modelUrlNormalizer;
112 
113     @Inject
114     private ModelValidator modelValidator;
115 
116     @Inject
117     private PluginConfigurationExpander pluginConfigurationExpander;
118 
119     @Inject
120     private PluginManagementInjector pluginManagementInjector;
121 
122     @Inject
123     private SuperPomProvider superPomProvider;
124 
125     @Inject
126     private ModelVersionParser versionParser;
127 
128     @Inject
129     private ModelTransformer modelTransformer;
130 
131     // To break circular dependency
132     @Inject
133     private Provider<RepositorySystem> repositorySystem;
134 
135     @Inject
136     private RemoteRepositoryManager remoteRepositoryManager;
137 
138     @Inject
139     private ProfileActivationFilePathInterpolator profileActivationFilePathInterpolator;
140 
141     Logger logger = LoggerFactory.getLogger(getClass());
142 
143     @Override
144     public Model build(RepositorySystemSession session, MavenProject project, Path src) throws ModelBuilderException {
145         Model model = project.getModel().getDelegate();
146         String packaging = model.getPackaging();
147         String originalPackaging = project.getOriginalModel().getPackaging();
148         if (POM_PACKAGING.equals(packaging) && !BOM_PACKAGING.equals(originalPackaging)) {
149             return buildPom(session, project, src);
150         } else {
151             return buildNonPom(session, project, src);
152         }
153     }
154 
155     protected Model buildPom(RepositorySystemSession session, MavenProject project, Path src)
156             throws ModelBuilderException {
157         ModelBuilderResult result = buildModel(session, project, src);
158         Model model = result.getRawModel();
159         return transform(model, project);
160     }
161 
162     protected Model buildNonPom(RepositorySystemSession session, MavenProject project, Path src)
163             throws ModelBuilderException {
164         ModelBuilderResult result = buildModel(session, project, src);
165         Model model = result.getEffectiveModel();
166         return transform(model, project);
167     }
168 
169     private ModelBuilderResult buildModel(RepositorySystemSession session, MavenProject project, Path src)
170             throws ModelBuilderException {
171         ProfileSelector customSelector = new DefaultProfileSelector() {
172             @Override
173             public List<Profile> getActiveProfiles(
174                     Collection<Profile> profiles, ProfileActivationContext context, ModelProblemCollector problems) {
175                 return new ArrayList<>();
176             }
177         };
178         DefaultModelBuilder modelBuilder = new DefaultModelBuilder(
179                 modelProcessor,
180                 modelValidator,
181                 modelNormalizer,
182                 modelInterpolator,
183                 modelPathTranslator,
184                 modelUrlNormalizer,
185                 superPomProvider,
186                 inheritanceAssembler,
187                 customSelector,
188                 profileInjector,
189                 pluginManagementInjector,
190                 dependencyManagementInjector,
191                 dependencyManagementImporter,
192                 lifecycleBindingsInjector,
193                 pluginConfigurationExpander,
194                 profileActivationFilePathInterpolator,
195                 modelTransformer,
196                 versionParser);
197         InternalSession iSession = InternalSession.from(session);
198         ModelBuilderRequest.ModelBuilderRequestBuilder request = ModelBuilderRequest.builder();
199         request.projectBuild(true);
200         request.session(iSession);
201         request.source(ModelSource.fromPath(src));
202         request.validationLevel(ModelBuilderRequest.VALIDATION_LEVEL_MINIMAL);
203         request.locationTracking(false);
204         request.modelResolver(iSession.getData().get(SessionData.key(ModelResolver.class)));
205         request.transformerContextBuilder(modelBuilder.newTransformerContextBuilder());
206         request.systemProperties(session.getSystemProperties());
207         request.userProperties(session.getUserProperties());
208         request.modelCache(DefaultModelCache.newInstance(session, false));
209         if (session.getCache() != null) {
210             Map<?, ?> map = (Map) session.getCache().get(session, DefaultModelCache.class.getName());
211             List<String> paths = map.keySet().stream()
212                     .map(Object::toString)
213                     .filter(s -> s.startsWith("SourceCacheKey"))
214                     .map(s -> s.substring("SourceCacheKey[location=".length(), s.indexOf(", tag")))
215                     .sorted()
216                     .distinct()
217                     .toList();
218             logger.debug("ModelCache contains " + paths.size());
219             paths.forEach(s -> logger.debug("    " + s));
220         }
221         return modelBuilder.build(request.build());
222     }
223 
224     static Model transform(Model model, MavenProject project) {
225         String packaging = model.getPackaging();
226         if (POM_PACKAGING.equals(packaging)) {
227             // raw to consumer transform
228             model = model.withRoot(false).withModules(null);
229             if (model.getParent() != null) {
230                 model = model.withParent(model.getParent().withRelativePath(null));
231             }
232 
233             if (!model.isPreserveModelVersion()) {
234                 model = model.withPreserveModelVersion(false);
235                 String modelVersion = new MavenModelVersion().getModelVersion(model);
236                 model = model.withModelVersion(modelVersion);
237             }
238         } else if (BOM_PACKAGING.equals(packaging)) {
239             DependencyManagement dependencyManagement =
240                     project.getOriginalModel().getDependencyManagement().getDelegate();
241             List<Dependency> dependencies = new ArrayList<>();
242             String version = model.getVersion();
243 
244             dependencyManagement
245                     .getDependencies()
246                     .forEach((dependency) -> dependencies.add(dependency.withVersion(version)));
247             Model.Builder builder = prune(
248                     Model.newBuilder(model, true)
249                             .preserveModelVersion(false)
250                             .root(false)
251                             .parent(null)
252                             .dependencyManagement(dependencyManagement.withDependencies(dependencies))
253                             .build(null),
254                     model);
255             builder.packaging(POM_PACKAGING);
256             builder.profiles(prune(model.getProfiles()));
257 
258             model = builder.build();
259             String modelVersion = new MavenModelVersion().getModelVersion(model);
260             model = model.withModelVersion(modelVersion);
261         } else {
262             Model.Builder builder = prune(
263                     Model.newBuilder(model, true)
264                             .preserveModelVersion(false)
265                             .root(false)
266                             .parent(null)
267                             .build(null),
268                     model);
269             builder.profiles(prune(model.getProfiles()));
270 
271             model = builder.build();
272             String modelVersion = new MavenModelVersion().getModelVersion(model);
273             model = model.withModelVersion(modelVersion);
274         }
275         return model;
276     }
277 
278     private static List<Profile> prune(List<Profile> profiles) {
279         return profiles.stream()
280                 .map(p -> {
281                     Profile.Builder builder = Profile.newBuilder(p, true);
282                     prune((ModelBase.Builder) builder, p);
283                     return builder.build(null).build();
284                 })
285                 .filter(p -> !isEmpty(p))
286                 .collect(Collectors.toList());
287     }
288 
289     private static boolean isEmpty(Profile profile) {
290         return profile.getActivation() == null
291                 && profile.getBuild() == null
292                 && profile.getDependencies().isEmpty()
293                 && (profile.getDependencyManagement() == null
294                         || profile.getDependencyManagement().getDependencies().isEmpty())
295                 && profile.getDistributionManagement() == null
296                 && profile.getModules().isEmpty()
297                 && profile.getProperties().isEmpty()
298                 && profile.getRepositories().isEmpty()
299                 && profile.getPluginRepositories().isEmpty()
300                 && profile.getReporting() == null;
301     }
302 
303     private static <T extends ModelBase.Builder> T prune(T builder, ModelBase model) {
304         builder.properties(null).reporting(null);
305         if (model.getDistributionManagement() != null
306                 && model.getDistributionManagement().getRelocation() != null) {
307             // keep relocation only
308             builder.distributionManagement(DistributionManagement.newBuilder()
309                     .relocation(model.getDistributionManagement().getRelocation())
310                     .build());
311         }
312         // only keep repositories other than 'central'
313         builder.pluginRepositories(pruneRepositories(model.getPluginRepositories()));
314         builder.repositories(pruneRepositories(model.getRepositories()));
315         return builder;
316     }
317 
318     private static List<Repository> pruneRepositories(List<Repository> repositories) {
319         return repositories.stream()
320                 .filter(r -> !org.apache.maven.api.Repository.CENTRAL_ID.equals(r.getId()))
321                 .collect(Collectors.toList());
322     }
323 }