View Javadoc
1   package org.apache.maven.toolchain.building;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.building.Problem;
23  import org.apache.maven.building.ProblemCollector;
24  import org.apache.maven.building.ProblemCollectorFactory;
25  import org.apache.maven.building.Source;
26  import org.apache.maven.toolchain.io.ToolchainsParseException;
27  import org.apache.maven.toolchain.io.ToolchainsReader;
28  import org.apache.maven.toolchain.io.ToolchainsWriter;
29  import org.apache.maven.toolchain.merge.MavenToolchainMerger;
30  import org.apache.maven.api.toolchain.PersistedToolchains;
31  import org.apache.maven.api.toolchain.TrackableBase;
32  import org.codehaus.plexus.interpolation.EnvarBasedValueSource;
33  import org.codehaus.plexus.interpolation.InterpolationException;
34  import org.codehaus.plexus.interpolation.RegexBasedInterpolator;
35  
36  import javax.inject.Inject;
37  import javax.inject.Named;
38  import javax.inject.Singleton;
39  import java.io.IOException;
40  import java.io.StringReader;
41  import java.io.StringWriter;
42  import java.util.Collections;
43  import java.util.List;
44  import java.util.Map;
45  
46  /**
47   *
48   * @author Robert Scholte
49   * @since 3.3.0
50   */
51  @Named
52  @Singleton
53  public class DefaultToolchainsBuilder
54      implements ToolchainsBuilder
55  {
56      private final MavenToolchainMerger toolchainsMerger = new MavenToolchainMerger();
57      private final ToolchainsWriter toolchainsWriter;
58      private final ToolchainsReader toolchainsReader;
59  
60      @Inject
61      public DefaultToolchainsBuilder(
62              ToolchainsWriter toolchainsWriter,
63              ToolchainsReader toolchainsReader )
64      {
65          this.toolchainsWriter = toolchainsWriter;
66          this.toolchainsReader = toolchainsReader;
67      }
68  
69      @Override
70      public ToolchainsBuildingResult build( ToolchainsBuildingRequest request )
71          throws ToolchainsBuildingException
72      {
73          ProblemCollector problems = ProblemCollectorFactory.newInstance( null );
74  
75          PersistedToolchains globalToolchains = readToolchains( request.getGlobalToolchainsSource(), request, problems );
76  
77          PersistedToolchains userToolchains = readToolchains( request.getUserToolchainsSource(), request, problems );
78  
79          PersistedToolchains merged = toolchainsMerger.merge(
80                  userToolchains, globalToolchains, TrackableBase.GLOBAL_LEVEL );
81  
82          problems.setSource( "" );
83  
84          merged = interpolate( merged, problems );
85  
86          if ( hasErrors( problems.getProblems() ) )
87          {
88              throw new ToolchainsBuildingException( problems.getProblems() );
89          }
90  
91          return new DefaultToolchainsBuildingResult(
92                  new org.apache.maven.toolchain.model.PersistedToolchains( merged ), problems.getProblems() );
93      }
94  
95      private PersistedToolchains interpolate( PersistedToolchains toolchains, ProblemCollector problems )
96      {
97  
98          StringWriter stringWriter = new StringWriter( 1024 * 4 );
99          try
100         {
101             toolchainsWriter.write( stringWriter, null, toolchains );
102         }
103         catch ( IOException e )
104         {
105             throw new IllegalStateException( "Failed to serialize toolchains to memory", e );
106         }
107 
108         String serializedToolchains = stringWriter.toString();
109 
110         RegexBasedInterpolator interpolator = new RegexBasedInterpolator();
111 
112         try
113         {
114             interpolator.addValueSource( new EnvarBasedValueSource() );
115         }
116         catch ( IOException e )
117         {
118             problems.add( Problem.Severity.WARNING, "Failed to use environment variables for interpolation: "
119                     + e.getMessage(), -1, -1, e );
120         }
121 
122         interpolator.addPostProcessor( ( expression, value ) ->
123         {
124             if ( value != null )
125             {
126                 // we're going to parse this back in as XML so we need to escape XML markup
127                 value = value.toString().replace( "&", "&amp;" ).replace( "<", "&lt;" ).replace( ">", "&gt;" );
128                 return value;
129             }
130             return null;
131         } );
132 
133         try
134         {
135             serializedToolchains = interpolator.interpolate( serializedToolchains );
136         }
137         catch ( InterpolationException e )
138         {
139             problems.add( Problem.Severity.ERROR, "Failed to interpolate toolchains: " + e.getMessage(), -1, -1, e );
140             return toolchains;
141         }
142 
143         PersistedToolchains result;
144         try
145         {
146             Map<String, ?> options = Collections.singletonMap( ToolchainsReader.IS_STRICT, Boolean.FALSE );
147 
148             result = toolchainsReader.read( new StringReader( serializedToolchains ), options );
149         }
150         catch ( IOException e )
151         {
152             problems.add( Problem.Severity.ERROR, "Failed to interpolate toolchains: " + e.getMessage(), -1, -1, e );
153             return toolchains;
154         }
155 
156         return result;
157     }
158 
159     private PersistedToolchains readToolchains( Source toolchainsSource, ToolchainsBuildingRequest request,
160                                                 ProblemCollector problems )
161     {
162         if ( toolchainsSource == null )
163         {
164             return PersistedToolchains.newInstance();
165         }
166 
167         PersistedToolchains toolchains;
168 
169         try
170         {
171             Map<String, ?> options = Collections.singletonMap( ToolchainsReader.IS_STRICT, Boolean.TRUE );
172 
173             try
174             {
175                 toolchains = toolchainsReader.read( toolchainsSource.getInputStream(), options );
176             }
177             catch ( ToolchainsParseException e )
178             {
179                 options = Collections.singletonMap( ToolchainsReader.IS_STRICT, Boolean.FALSE );
180 
181                 toolchains = toolchainsReader.read( toolchainsSource.getInputStream(), options );
182 
183                 problems.add( Problem.Severity.WARNING, e.getMessage(), e.getLineNumber(), e.getColumnNumber(),
184                               e );
185             }
186         }
187         catch ( ToolchainsParseException e )
188         {
189             problems.add( Problem.Severity.FATAL, "Non-parseable toolchains " + toolchainsSource.getLocation()
190                 + ": " + e.getMessage(), e.getLineNumber(), e.getColumnNumber(), e );
191             return PersistedToolchains.newInstance();
192         }
193         catch ( IOException e )
194         {
195             problems.add( Problem.Severity.FATAL, "Non-readable toolchains " + toolchainsSource.getLocation()
196                 + ": " + e.getMessage(), -1, -1, e );
197             return PersistedToolchains.newInstance();
198         }
199 
200         return toolchains;
201     }
202 
203     private boolean hasErrors( List<Problem> problems )
204     {
205         if ( problems != null )
206         {
207             for ( Problem problem : problems )
208             {
209                 if ( Problem.Severity.ERROR.compareTo( problem.getSeverity() ) >= 0 )
210                 {
211                     return true;
212                 }
213             }
214         }
215 
216         return false;
217     }
218 }