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