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.toolchain.building;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.io.IOException;
26  import java.io.StringReader;
27  import java.io.StringWriter;
28  import java.util.Collections;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.apache.maven.building.Problem;
33  import org.apache.maven.building.ProblemCollector;
34  import org.apache.maven.building.ProblemCollectorFactory;
35  import org.apache.maven.building.Source;
36  import org.apache.maven.toolchain.io.ToolchainsParseException;
37  import org.apache.maven.toolchain.io.ToolchainsReader;
38  import org.apache.maven.toolchain.io.ToolchainsWriter;
39  import org.apache.maven.toolchain.merge.MavenToolchainMerger;
40  import org.apache.maven.toolchain.model.PersistedToolchains;
41  import org.apache.maven.toolchain.model.TrackableBase;
42  import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
43  import org.codehaus.plexus.interpolation.InterpolationException;
44  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
45  
46  /**
47   *
48   * @author Robert Scholte
49   * @since 3.3.0
50   */
51  @Named
52  @Singleton
53  public class DefaultToolchainsBuilder implements ToolchainsBuilder {
54      private final MavenToolchainMerger toolchainsMerger = new MavenToolchainMerger();
55      private final ToolchainsWriter toolchainsWriter;
56      private final ToolchainsReader toolchainsReader;
57  
58      @Inject
59      public DefaultToolchainsBuilder(ToolchainsWriter toolchainsWriter, ToolchainsReader toolchainsReader) {
60          this.toolchainsWriter = toolchainsWriter;
61          this.toolchainsReader = toolchainsReader;
62      }
63  
64      @Override
65      public ToolchainsBuildingResult build(ToolchainsBuildingRequest request) throws ToolchainsBuildingException {
66          ProblemCollector problems = ProblemCollectorFactory.newInstance(null);
67  
68          PersistedToolchains globalToolchains = readToolchains(request.getGlobalToolchainsSource(), request, problems);
69  
70          PersistedToolchains userToolchains = readToolchains(request.getUserToolchainsSource(), request, problems);
71  
72          toolchainsMerger.merge(userToolchains, globalToolchains, TrackableBase.GLOBAL_LEVEL);
73  
74          problems.setSource("");
75  
76          userToolchains = interpolate(userToolchains, problems);
77  
78          if (hasErrors(problems.getProblems())) {
79              throw new ToolchainsBuildingException(problems.getProblems());
80          }
81  
82          return new DefaultToolchainsBuildingResult(userToolchains, problems.getProblems());
83      }
84  
85      private PersistedToolchains interpolate(PersistedToolchains toolchains, ProblemCollector problems) {
86  
87          StringWriter stringWriter = new StringWriter(1024 * 4);
88          try {
89              toolchainsWriter.write(stringWriter, null, toolchains);
90          } catch (IOException e) {
91              throw new IllegalStateException("Failed to serialize toolchains to memory", e);
92          }
93  
94          String serializedToolchains = stringWriter.toString();
95  
96          RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
97  
98          try {
99              interpolator.addValueSource(new EnvarBasedValueSource());
100         } catch (IOException e) {
101             problems.add(
102                     Problem.Severity.WARNING,
103                     "Failed to use environment variables for interpolation: " + e.getMessage(),
104                     -1,
105                     -1,
106                     e);
107         }
108 
109         interpolator.addPostProcessor((expression, value) -> {
110             if (value != null) {
111                 // we're going to parse this back in as XML so we need to escape XML markup
112                 value = value.toString()
113                         .replace("&", "&")
114                         .replace("<", "&lt;")
115                         .replace(">", "&gt;");
116                 return value;
117             }
118             return null;
119         });
120 
121         try {
122             serializedToolchains = interpolator.interpolate(serializedToolchains);
123         } catch (InterpolationException e) {
124             problems.add(Problem.Severity.ERROR, "Failed to interpolate toolchains: " + e.getMessage(), -1, -1, e);
125             return toolchains;
126         }
127 
128         PersistedToolchains result;
129         try {
130             Map<String, ?> options = Collections.singletonMap(ToolchainsReader.IS_STRICT, Boolean.FALSE);
131 
132             result = toolchainsReader.read(new StringReader(serializedToolchains), options);
133         } catch (IOException e) {
134             problems.add(Problem.Severity.ERROR, "Failed to interpolate toolchains: " + e.getMessage(), -1, -1, e);
135             return toolchains;
136         }
137 
138         return result;
139     }
140 
141     private PersistedToolchains readToolchains(
142             Source toolchainsSource, ToolchainsBuildingRequest request, ProblemCollector problems) {
143         if (toolchainsSource == null) {
144             return new PersistedToolchains();
145         }
146 
147         PersistedToolchains toolchains;
148 
149         try {
150             Map<String, ?> options = Collections.singletonMap(ToolchainsReader.IS_STRICT, Boolean.TRUE);
151 
152             try {
153                 toolchains = toolchainsReader.read(toolchainsSource.getInputStream(), options);
154             } catch (ToolchainsParseException e) {
155                 options = Collections.singletonMap(ToolchainsReader.IS_STRICT, Boolean.FALSE);
156 
157                 toolchains = toolchainsReader.read(toolchainsSource.getInputStream(), options);
158 
159                 problems.add(Problem.Severity.WARNING, e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e);
160             }
161         } catch (ToolchainsParseException e) {
162             problems.add(
163                     Problem.Severity.FATAL,
164                     "Non-parseable toolchains " + toolchainsSource.getLocation() + ": " + e.getMessage(),
165                     e.getLineNumber(),
166                     e.getColumnNumber(),
167                     e);
168             return new PersistedToolchains();
169         } catch (IOException e) {
170             problems.add(
171                     Problem.Severity.FATAL,
172                     "Non-readable toolchains " + toolchainsSource.getLocation() + ": " + e.getMessage(),
173                     -1,
174                     -1,
175                     e);
176             return new PersistedToolchains();
177         }
178 
179         return toolchains;
180     }
181 
182     private boolean hasErrors(List<Problem> problems) {
183         if (problems != null) {
184             for (Problem problem : problems) {
185                 if (Problem.Severity.ERROR.compareTo(problem.getSeverity()) >= 0) {
186                     return true;
187                 }
188             }
189         }
190 
191         return false;
192     }
193 }