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