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  
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.util.ArrayList;
30  import java.util.List;
31  import java.util.Objects;
32  import java.util.stream.Collectors;
33  
34  import org.apache.maven.api.SessionData;
35  import org.apache.maven.api.model.Dependency;
36  import org.apache.maven.api.model.DependencyManagement;
37  import org.apache.maven.api.model.DistributionManagement;
38  import org.apache.maven.api.model.Model;
39  import org.apache.maven.api.model.ModelBase;
40  import org.apache.maven.api.model.Profile;
41  import org.apache.maven.api.model.Repository;
42  import org.apache.maven.api.services.ModelBuilder;
43  import org.apache.maven.api.services.ModelBuilderException;
44  import org.apache.maven.api.services.ModelBuilderRequest;
45  import org.apache.maven.api.services.ModelBuilderResult;
46  import org.apache.maven.api.services.ModelSource;
47  import org.apache.maven.api.services.Source;
48  import org.apache.maven.api.services.model.LifecycleBindingsInjector;
49  import org.apache.maven.internal.impl.InternalSession;
50  import org.apache.maven.model.v4.MavenModelVersion;
51  import org.apache.maven.project.MavenProject;
52  import org.eclipse.aether.RepositorySystemSession;
53  
54  @Named
55  class DefaultConsumerPomBuilder implements ConsumerPomBuilder {
56      private static final String BOM_PACKAGING = "bom";
57  
58      public static final String POM_PACKAGING = "pom";
59  
60      private final LifecycleBindingsInjector lifecycleBindingsInjector;
61  
62      @Inject
63      @SuppressWarnings("checkstyle:ParameterNumber")
64      DefaultConsumerPomBuilder(LifecycleBindingsInjector lifecycleBindingsInjector) {
65          this.lifecycleBindingsInjector = lifecycleBindingsInjector;
66      }
67  
68      @Override
69      public Model build(RepositorySystemSession session, MavenProject project, Path src) throws ModelBuilderException {
70          Model model = project.getModel().getDelegate();
71          String packaging = model.getPackaging();
72          String originalPackaging = project.getOriginalModel().getPackaging();
73          if (POM_PACKAGING.equals(packaging) && !BOM_PACKAGING.equals(originalPackaging)) {
74              return buildPom(session, project, src);
75          } else {
76              return buildNonPom(session, project, src);
77          }
78      }
79  
80      protected Model buildPom(RepositorySystemSession session, MavenProject project, Path src)
81              throws ModelBuilderException {
82          ModelBuilderResult result = buildModel(session, project, src);
83          Model model = result.getRawModel();
84          return transform(model, project);
85      }
86  
87      protected Model buildNonPom(RepositorySystemSession session, MavenProject project, Path src)
88              throws ModelBuilderException {
89          ModelBuilderResult result = buildModel(session, project, src);
90          Model model = result.getEffectiveModel();
91          return transform(model, project);
92      }
93  
94      private ModelBuilderResult buildModel(RepositorySystemSession session, MavenProject project, Path src)
95              throws ModelBuilderException {
96          InternalSession iSession = InternalSession.from(session);
97          ModelBuilderRequest.ModelBuilderRequestBuilder request = ModelBuilderRequest.builder();
98          request.requestType(ModelBuilderRequest.RequestType.CONSUMER_POM);
99          request.session(iSession);
100         // in order to resolve parents, we need to fake being at the correct location
101         request.source(new PomConsumerModelSource(project.getModel().getPomPath(), src));
102         request.locationTracking(false);
103         request.systemProperties(session.getSystemProperties());
104         request.userProperties(session.getUserProperties());
105         request.lifecycleBindingsInjector(lifecycleBindingsInjector::injectLifecycleBindings);
106         ModelBuilder.ModelBuilderSession mbSession =
107                 iSession.getData().get(SessionData.key(ModelBuilder.ModelBuilderSession.class));
108         return mbSession.build(request.build());
109     }
110 
111     static Model transform(Model model, MavenProject project) {
112         String packaging = model.getPackaging();
113         if (POM_PACKAGING.equals(packaging)) {
114             // raw to consumer transform
115             model = model.withRoot(false).withModules(null).withSubprojects(null);
116             if (model.getParent() != null) {
117                 model = model.withParent(model.getParent().withRelativePath(null));
118             }
119 
120             if (!model.isPreserveModelVersion()) {
121                 model = model.withPreserveModelVersion(false);
122                 String modelVersion = new MavenModelVersion().getModelVersion(model);
123                 model = model.withModelVersion(modelVersion);
124             }
125         } else if (BOM_PACKAGING.equals(packaging)) {
126             DependencyManagement dependencyManagement =
127                     project.getOriginalModel().getDependencyManagement().getDelegate();
128             List<Dependency> dependencies = new ArrayList<>();
129             String version = model.getVersion();
130 
131             dependencyManagement
132                     .getDependencies()
133                     .forEach((dependency) -> dependencies.add(dependency.withVersion(version)));
134             Model.Builder builder = prune(
135                     Model.newBuilder(model, true)
136                             .preserveModelVersion(false)
137                             .root(false)
138                             .parent(null)
139                             .dependencyManagement(dependencyManagement.withDependencies(dependencies))
140                             .build(null),
141                     model);
142             builder.packaging(POM_PACKAGING);
143             builder.profiles(prune(model.getProfiles()));
144 
145             model = builder.build();
146             String modelVersion = new MavenModelVersion().getModelVersion(model);
147             model = model.withModelVersion(modelVersion);
148         } else {
149             Model.Builder builder = prune(
150                     Model.newBuilder(model, true)
151                             .preserveModelVersion(false)
152                             .root(false)
153                             .parent(null)
154                             .build(null),
155                     model);
156             builder.profiles(prune(model.getProfiles()));
157 
158             model = builder.build();
159             String modelVersion = new MavenModelVersion().getModelVersion(model);
160             model = model.withModelVersion(modelVersion);
161         }
162         return model;
163     }
164 
165     private static List<Profile> prune(List<Profile> profiles) {
166         return profiles.stream()
167                 .map(p -> {
168                     Profile.Builder builder = Profile.newBuilder(p, true);
169                     prune((ModelBase.Builder) builder, p);
170                     return builder.build(null).build();
171                 })
172                 .filter(p -> !isEmpty(p))
173                 .collect(Collectors.toList());
174     }
175 
176     private static boolean isEmpty(Profile profile) {
177         return profile.getActivation() == null
178                 && profile.getBuild() == null
179                 && profile.getDependencies().isEmpty()
180                 && (profile.getDependencyManagement() == null
181                         || profile.getDependencyManagement().getDependencies().isEmpty())
182                 && profile.getDistributionManagement() == null
183                 && profile.getModules().isEmpty()
184                 && profile.getSubprojects().isEmpty()
185                 && profile.getProperties().isEmpty()
186                 && profile.getRepositories().isEmpty()
187                 && profile.getPluginRepositories().isEmpty()
188                 && profile.getReporting() == null;
189     }
190 
191     private static <T extends ModelBase.Builder> T prune(T builder, ModelBase model) {
192         builder.properties(null).reporting(null);
193         if (model.getDistributionManagement() != null
194                 && model.getDistributionManagement().getRelocation() != null) {
195             // keep relocation only
196             builder.distributionManagement(DistributionManagement.newBuilder()
197                     .relocation(model.getDistributionManagement().getRelocation())
198                     .build());
199         }
200         // only keep repositories other than 'central'
201         builder.pluginRepositories(pruneRepositories(model.getPluginRepositories()));
202         builder.repositories(pruneRepositories(model.getRepositories()));
203         return builder;
204     }
205 
206     private static List<Repository> pruneRepositories(List<Repository> repositories) {
207         return repositories.stream()
208                 .filter(r -> !org.apache.maven.api.Repository.CENTRAL_ID.equals(r.getId()))
209                 .collect(Collectors.toList());
210     }
211 
212     static class PomConsumerModelSource implements ModelSource {
213         final Path path;
214         final Path src;
215 
216         PomConsumerModelSource(Path path, Path src) {
217             this.path = path;
218             this.src = src;
219         }
220 
221         @Override
222         public Path getPath() {
223             return path;
224         }
225 
226         @Override
227         public InputStream openStream() throws IOException {
228             return Files.newInputStream(src);
229         }
230 
231         @Override
232         public String getLocation() {
233             return src.toString();
234         }
235 
236         @Override
237         public Source resolve(String relative) {
238             return ModelSource.fromPath(path.resolve(relative));
239         }
240 
241         @Override
242         public ModelSource resolve(ModelLocator locator, String relative) {
243             String norm = relative.replace('\\', File.separatorChar).replace('/', File.separatorChar);
244             Path path = getPath().getParent().resolve(norm);
245             Path relatedPom = locator.locateExistingPom(path);
246             if (relatedPom != null) {
247                 return ModelSource.fromPath(relatedPom);
248             }
249             return null;
250         }
251 
252         @Override
253         public boolean equals(Object o) {
254             return this == o
255                     || o.getClass() == getClass()
256                             && Objects.equals(path, ((PomConsumerModelSource) o).path)
257                             && Objects.equals(src, ((PomConsumerModelSource) o).src);
258         }
259 
260         @Override
261         public int hashCode() {
262             return Objects.hash(path, src);
263         }
264 
265         @Override
266         public String toString() {
267             return "PomConsumerModelSource[" + "path=" + path + ']';
268         }
269     }
270 }