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.impl;
20  
21  import javax.xml.stream.Location;
22  import javax.xml.stream.XMLStreamException;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.util.Map;
27  import java.util.function.UnaryOperator;
28  
29  import org.apache.maven.api.di.Inject;
30  import org.apache.maven.api.di.Named;
31  import org.apache.maven.api.services.BuilderProblem;
32  import org.apache.maven.api.services.Interpolator;
33  import org.apache.maven.api.services.ProblemCollector;
34  import org.apache.maven.api.services.Source;
35  import org.apache.maven.api.services.ToolchainsBuilder;
36  import org.apache.maven.api.services.ToolchainsBuilderException;
37  import org.apache.maven.api.services.ToolchainsBuilderRequest;
38  import org.apache.maven.api.services.ToolchainsBuilderResult;
39  import org.apache.maven.api.services.xml.ToolchainsXmlFactory;
40  import org.apache.maven.api.services.xml.XmlReaderException;
41  import org.apache.maven.api.services.xml.XmlReaderRequest;
42  import org.apache.maven.api.toolchain.PersistedToolchains;
43  import org.apache.maven.toolchain.v4.MavenToolchainsMerger;
44  import org.apache.maven.toolchain.v4.MavenToolchainsTransformer;
45  
46  /**
47   * Builds the effective toolchains from a user toolchains file and/or a global toolchains file.
48   *
49   */
50  @Named
51  public class DefaultToolchainsBuilder implements ToolchainsBuilder {
52  
53      private final MavenToolchainsMerger toolchainsMerger = new MavenToolchainsMerger();
54  
55      private final Interpolator interpolator;
56  
57      private final ToolchainsXmlFactory toolchainsXmlFactory;
58  
59      @Inject
60      public DefaultToolchainsBuilder(Interpolator interpolator, ToolchainsXmlFactory toolchainsXmlFactory) {
61          this.interpolator = interpolator;
62          this.toolchainsXmlFactory = toolchainsXmlFactory;
63      }
64  
65      @Override
66      public ToolchainsBuilderResult build(ToolchainsBuilderRequest request) throws ToolchainsBuilderException {
67          ProblemCollector<BuilderProblem> problems = ProblemCollector.create(request.getSession());
68  
69          Source installationSource = request.getInstallationToolchainsSource().orElse(null);
70          PersistedToolchains installation = readToolchains(installationSource, request, problems);
71  
72          Source userSource = request.getUserToolchainsSource().orElse(null);
73          PersistedToolchains user = readToolchains(userSource, request, problems);
74  
75          PersistedToolchains effective = toolchainsMerger.merge(user, installation, false, null);
76  
77          if (problems.hasErrorProblems()) {
78              throw new ToolchainsBuilderException("Error building toolchains", problems);
79          }
80  
81          return new DefaultToolchainsBuilderResult(request, effective, problems);
82      }
83  
84      private PersistedToolchains readToolchains(
85              Source toolchainsSource, ToolchainsBuilderRequest request, ProblemCollector<BuilderProblem> problems) {
86          if (toolchainsSource == null) {
87              return PersistedToolchains.newInstance();
88          }
89  
90          PersistedToolchains toolchains;
91  
92          try {
93              try {
94                  InputStream is = toolchainsSource.openStream();
95                  if (is == null) {
96                      return PersistedToolchains.newInstance();
97                  }
98                  toolchains = toolchainsXmlFactory.read(XmlReaderRequest.builder()
99                          .inputStream(is)
100                         .location(toolchainsSource.getLocation())
101                         .strict(true)
102                         .build());
103             } catch (XmlReaderException e) {
104                 InputStream is = toolchainsSource.openStream();
105                 if (is == null) {
106                     return PersistedToolchains.newInstance();
107                 }
108                 toolchains = toolchainsXmlFactory.read(XmlReaderRequest.builder()
109                         .inputStream(is)
110                         .location(toolchainsSource.getLocation())
111                         .strict(false)
112                         .build());
113                 Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
114                 problems.reportProblem(new DefaultBuilderProblem(
115                         toolchainsSource.getLocation(),
116                         loc != null ? loc.getLineNumber() : -1,
117                         loc != null ? loc.getColumnNumber() : -1,
118                         e,
119                         e.getMessage(),
120                         BuilderProblem.Severity.WARNING));
121             }
122         } catch (XmlReaderException e) {
123             Location loc = e.getCause() instanceof XMLStreamException xe ? xe.getLocation() : null;
124             problems.reportProblem(new DefaultBuilderProblem(
125                     toolchainsSource.getLocation(),
126                     loc != null ? loc.getLineNumber() : -1,
127                     loc != null ? loc.getColumnNumber() : -1,
128                     e,
129                     "Non-parseable toolchains " + toolchainsSource.getLocation() + ": " + e.getMessage(),
130                     BuilderProblem.Severity.FATAL));
131             return PersistedToolchains.newInstance();
132         } catch (IOException e) {
133             problems.reportProblem(new DefaultBuilderProblem(
134                     toolchainsSource.getLocation(),
135                     -1,
136                     -1,
137                     e,
138                     "Non-readable toolchains " + toolchainsSource.getLocation() + ": " + e.getMessage(),
139                     BuilderProblem.Severity.FATAL));
140             return PersistedToolchains.newInstance();
141         }
142 
143         toolchains = interpolate(toolchains, request, problems);
144 
145         return toolchains;
146     }
147 
148     private PersistedToolchains interpolate(
149             PersistedToolchains toolchains,
150             ToolchainsBuilderRequest request,
151             ProblemCollector<BuilderProblem> problems) {
152         Map<String, String> userProperties = request.getSession().getUserProperties();
153         Map<String, String> systemProperties = request.getSession().getSystemProperties();
154         UnaryOperator<String> src = Interpolator.chain(userProperties::get, systemProperties::get);
155         return new MavenToolchainsTransformer(value -> value != null ? interpolator.interpolate(value, src) : null)
156                 .visit(toolchains);
157     }
158 
159     /**
160      * Collects the output of the toolchains builder.
161      *
162      */
163     static class DefaultToolchainsBuilderResult implements ToolchainsBuilderResult {
164 
165         private final ToolchainsBuilderRequest request;
166 
167         private final PersistedToolchains effectiveToolchains;
168 
169         private final ProblemCollector<BuilderProblem> problems;
170 
171         DefaultToolchainsBuilderResult(
172                 ToolchainsBuilderRequest request,
173                 PersistedToolchains effectiveToolchains,
174                 ProblemCollector<BuilderProblem> problems) {
175             this.request = request;
176             this.effectiveToolchains = effectiveToolchains;
177             this.problems = (problems != null) ? problems : ProblemCollector.empty();
178         }
179 
180         @Override
181         public ToolchainsBuilderRequest getRequest() {
182             return request;
183         }
184 
185         @Override
186         public PersistedToolchains getEffectiveToolchains() {
187             return effectiveToolchains;
188         }
189 
190         @Override
191         public ProblemCollector<BuilderProblem> getProblems() {
192             return problems;
193         }
194     }
195 }