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