View Javadoc
1   package org.apache.maven.plugins.invoker;
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 java.io.File;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.HashMap;
27  import java.util.Map;
28  import java.util.Properties;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  
32  import org.apache.maven.shared.invoker.InvocationRequest;
33  import org.apache.maven.shared.invoker.InvocationRequest.ReactorFailureBehavior;
34  import org.codehaus.plexus.util.StringUtils;
35  
36  /**
37   * Provides a convenient facade around the <code>invoker.properties</code>.
38   *
39   * @author Benjamin Bentmann
40   */
41  class InvokerProperties
42  {
43      private static final String SELECTOR_PREFIX = "selector.";
44  
45      private enum InvocationProperty
46      {
47          PROJECT( "invoker.project" ),
48          GOALS( "invoker.goals" ),
49          PROFILES( "invoker.profiles" ),
50          MAVEN_OPTS( "invoker.mavenOpts" ),
51          FAILURE_BEHAVIOR( "invoker.failureBehavior" ),
52          NON_RECURSIVE( "invoker.nonRecursive" ),
53          OFFLINE( "invoker.offline" ),
54          SYSTEM_PROPERTIES_FILE( "invoker.systemPropertiesFile" ),
55          DEBUG( "invoker.debug" ),
56          SETTINGS_FILE ( "invoker.settingsFile" ),
57          TIMEOUT_IN_SECONDS ( "invoker.timeoutInSeconds" ),
58          ORDINAL ( "invoker.ordinal" );
59  
60          private final String key;
61  
62          InvocationProperty( final String s )
63          {
64              this.key = s;
65          }
66  
67          @Override
68          public String toString()
69          {
70              return key;
71          }
72      }
73      
74      private enum SelectorProperty
75      {
76          JAVA_VERSION( ".java.version" ),
77          MAVEN_VERSION( ".maven.version" ),
78          OS_FAMLY( ".os.family" );
79          
80          private final String suffix;
81          
82          SelectorProperty( String suffix )
83          {
84              this.suffix = suffix;
85          }
86          
87          @Override
88          public String toString()
89          {
90              return suffix;
91          }
92      }
93  
94      /**
95       * The invoker properties being wrapped.
96       */
97      private final Properties properties;
98  
99      /**
100      * Creates a new facade for the specified invoker properties. The properties will not be copied, so any changes to
101      * them will be reflected by the facade.
102      *
103      * @param properties The invoker properties to wrap, may be <code>null</code> if none.
104      */
105     InvokerProperties( Properties properties )
106     {
107         this.properties = ( properties != null ) ? properties : new Properties();
108     }
109 
110     /**
111      * Gets the invoker properties being wrapped.
112      *
113      * @return The invoker properties being wrapped, never <code>null</code>.
114      */
115     public Properties getProperties()
116     {
117         return this.properties;
118     }
119 
120     /**
121      * Gets the name of the corresponding build job.
122      *
123      * @return The name of the build job or an empty string if not set.
124      */
125     public String getJobName()
126     {
127         return this.properties.getProperty( "invoker.name", "" );
128     }
129 
130     /**
131      * Gets the description of the corresponding build job.
132      *
133      * @return The description of the build job or an empty string if not set.
134      */
135     public String getJobDescription()
136     {
137         return this.properties.getProperty( "invoker.description", "" );
138     }
139 
140     /**
141      * Get the corresponding ordinal value
142      * @return The ordinal value
143      */
144     public int getOrdinal()
145     {
146         return Integer.parseInt( this.properties.getProperty( "invoker.ordinal", "0" ) );
147     }
148 
149     /**
150      * Gets the specification of JRE versions on which this build job should be run.
151      *
152      * @return The specification of JRE versions or an empty string if not set.
153      */
154     public String getJreVersion()
155     {
156         return this.properties.getProperty( "invoker.java.version", "" );
157     }
158 
159     /**
160      * Gets the specification of JRE versions on which this build job should be run.
161      *
162      * @return The specification of JRE versions or an empty string if not set.
163      */
164     public String getJreVersion( int index )
165     {
166         return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.JAVA_VERSION.suffix,
167                                             getJreVersion() );
168     }
169 
170     /**
171      * Gets the specification of Maven versions on which this build job should be run.
172      *
173      * @return The specification of Maven versions on which this build job should be run.
174      * @since 1.5
175      */
176     public String getMavenVersion()
177     {
178         return this.properties.getProperty( "invoker.maven.version", "" );
179     }
180     
181     /**
182      * 
183      * @param index the selector index
184      * @return The specification of Maven versions on which this build job should be run.
185      * @since 3.0.0
186      */
187     public String getMavenVersion( int index )
188     {
189         return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.MAVEN_VERSION.suffix,
190                                             getMavenVersion() );
191     }
192 
193     /**
194      * Gets the specification of OS families on which this build job should be run.
195      *
196      * @return The specification of OS families or an empty string if not set.
197      */
198     public String getOsFamily()
199     {
200         return this.properties.getProperty( "invoker.os.family", "" );
201     }
202     
203     /**
204      * Gets the specification of OS families on which this build job should be run.
205      *
206      * @param index the selector index
207      * @return The specification of OS families or an empty string if not set.
208      * @since 3.0.0
209      */
210     public String getOsFamily( int index )
211     {
212         return this.properties.getProperty( SELECTOR_PREFIX + index + SelectorProperty.OS_FAMLY.suffix,
213                                             getOsFamily() );
214     }
215     
216     public Collection<InvokerToolchain> getToolchains()
217     {
218         return getToolchains( Pattern.compile( "invoker\\.toolchain\\.([^\\.]+)\\.(.+)" ) );
219     }
220 
221     public Collection<InvokerToolchain> getToolchains( int index )
222     {
223         return getToolchains( Pattern.compile( "selector\\." + index + "\\.invoker\\.toolchain\\.([^\\.]+)\\.(.+)" ) );
224     }
225 
226     private Collection<InvokerToolchain> getToolchains( Pattern p )
227     {
228         Map<String, InvokerToolchain> toolchains = new HashMap<>();
229         for ( Map.Entry<Object, Object> entry : this.properties.entrySet() )
230         {
231             Matcher m = p.matcher( entry.getKey().toString() );
232             if ( m.matches() )
233             {
234                 String type = m.group( 1 );
235                 String providesKey = m.group( 2 );
236                 String providesValue = entry.getValue().toString();
237 
238                 InvokerToolchain tc = toolchains.get( type );
239                 if ( tc == null )
240                 {
241                     tc = new InvokerToolchain( type );
242                     toolchains.put( type, tc );
243                 }
244                 tc.addProvides( providesKey, providesValue );
245             }
246         }
247         return toolchains.values();
248     }
249 
250     /**
251      * Determines whether these invoker properties contain a build definition for the specified invocation index.
252      *
253      * @param index The one-based index of the invocation to check for, must not be negative.
254      * @return <code>true</code> if the invocation with the specified index is defined, <code>false</code> otherwise.
255      */
256     public boolean isInvocationDefined( int index )
257     {
258         for ( InvocationProperty prop : InvocationProperty.values() )
259         {
260             if ( properties.getProperty( prop.toString() + '.' + index ) != null )
261             {
262                 return true;
263             }
264         }
265         return false;
266     }
267     
268     /**
269      * Determines whether these invoker properties contain a build definition for the specified selector index.
270      * 
271      * @param index the index
272      * @return <code>true</code> if the selector with the specified index is defined, <code>false</code> otherwise.
273      * @since 3.0.0
274      */
275     public boolean isSelectorDefined( int index )
276     {
277         for ( SelectorProperty prop : SelectorProperty.values() )
278         {
279             if ( properties.getProperty( SELECTOR_PREFIX + index + prop.suffix ) != null )
280             {
281                 return true;
282             }
283         }
284         return false;
285     }
286 
287     /**
288      * Configures the specified invocation request from these invoker properties. Settings not present in the invoker
289      * properties will be left unchanged in the invocation request.
290      *
291      * @param request The invocation request to configure, must not be <code>null</code>.
292      * @param index The one-based index of the invocation to configure, must not be negative.
293      */
294     public void configureInvocation( InvocationRequest request, int index )
295     {
296         String project = get( InvocationProperty.PROJECT, index );
297         if ( project != null )
298         {
299             File file = new File( request.getBaseDirectory(), project );
300             if ( file.isFile() )
301             {
302                 request.setBaseDirectory( file.getParentFile() );
303                 request.setPomFile( file );
304             }
305             else
306             {
307                 request.setBaseDirectory( file );
308                 request.setPomFile( null );
309             }
310         }
311 
312         String goals = get( InvocationProperty.GOALS, index );
313         if ( goals != null )
314         {
315             request.setGoals( new ArrayList<>( Arrays.asList( StringUtils.split( goals, ", \t\n\r\f" ) ) ) );
316         }
317 
318         String profiles = get( InvocationProperty.PROFILES, index );
319         if ( profiles != null )
320         {
321             // CHECKSTYLE_OFF: LineLength
322             request.setProfiles( new ArrayList<>( Arrays.asList( StringUtils.split( profiles,
323                                                                                           ", \t\n\r\f" ) ) ) );
324             // CHECKSTYLE_ON: LineLength
325         }
326 
327         String mvnOpts = get( InvocationProperty.MAVEN_OPTS, index );
328         if ( mvnOpts != null )
329         {
330             request.setMavenOpts( mvnOpts );
331         }
332 
333         String failureBehavior = get( InvocationProperty.FAILURE_BEHAVIOR, index );
334         if ( failureBehavior != null )
335         {
336             ReactorFailureBehavior valueOf =
337                 InvocationRequest.ReactorFailureBehavior.valueOfByLongOption( failureBehavior );
338             request.setReactorFailureBehavior( valueOf );
339         }
340 
341         String nonRecursive = get( InvocationProperty.NON_RECURSIVE, index );
342         if ( nonRecursive != null )
343         {
344             request.setRecursive( !Boolean.valueOf( nonRecursive ) );
345         }
346 
347         String offline = get( InvocationProperty.OFFLINE, index );
348         if ( offline != null )
349         {
350             request.setOffline( Boolean.valueOf( offline ) );
351         }
352 
353         String debug = get( InvocationProperty.DEBUG, index );
354         if ( debug != null )
355         {
356             request.setDebug( Boolean.valueOf( debug ) );
357         }
358     }
359 
360     /**
361      * Checks whether the specified exit code matches the one expected for the given invocation.
362      *
363      * @param exitCode The exit code of the Maven invocation to check.
364      * @param index The index of the invocation for which to check the exit code, must not be negative.
365      * @return <code>true</code> if the exit code is zero and a success was expected or if the exit code is non-zero and
366      *         a failue was expected, <code>false</code> otherwise.
367      */
368     public boolean isExpectedResult( int exitCode, int index )
369     {
370         boolean nonZeroExit = "failure".equalsIgnoreCase( get( "invoker.buildResult", index ) );
371         return ( exitCode != 0 ) == nonZeroExit;
372     }
373 
374     /**
375      * Gets the path to the properties file used to set the system properties for the specified invocation.
376      *
377      * @param index The index of the invocation, must not be negative.
378      * @return The path to the properties file or <code>null</code> if not set.
379      */
380     public String getSystemPropertiesFile( int index )
381     {
382         return get( InvocationProperty.SYSTEM_PROPERTIES_FILE, index );
383     }
384 
385     /**
386      * Gets the settings file used for the specified invocation.
387      * 
388      * @param index The index of the invocation, must not be negative.
389      * @return the value for the settings file or <code>null</code> if not set.
390      */
391     public String getSettingsFile( int index )
392     {
393         return get( InvocationProperty.SETTINGS_FILE, index );
394     }
395 
396     /**
397      * Get timeout to execute the project
398      * @param index index The index of the invocation, must not be negative.
399      * @return the value for the timeout or -1
400      */
401     public int getTimeoutInSeconds( int index )
402     {
403         String timeoutInSecondsStr = get( InvocationProperty.TIMEOUT_IN_SECONDS, index );
404         if ( StringUtils.isEmpty( timeoutInSecondsStr ) )
405         {
406             return -1;
407         }
408         // catch NumberFormatException? well we assume users knows what they do :-)
409         return Integer.parseInt( timeoutInSecondsStr );
410     }
411     /**
412      * Gets a value from the invoker properties. The invoker properties are intended to describe the invocation settings
413      * for multiple builds of the same project. For this reason, the properties are indexed. First, a property named
414      * <code>key.index</code> will be queried. If this property does not exist, the value of the property named
415      * <code>key</code> will finally be returned.
416      *
417      * @param key The (base) key for the invoker property to lookup, must not be <code>null</code>.
418      * @param index The index of the invocation for which to retrieve the value, must not be negative.
419      * @return The value for the requested invoker property or <code>null</code> if not defined.
420      */
421     String get( String key, int index )
422     {
423         if ( index < 0 )
424         {
425             throw new IllegalArgumentException( "invalid invocation index: " + index );
426         }
427 
428         String value = properties.getProperty( key + '.' + index );
429         if ( value == null )
430         {
431             value = properties.getProperty( key );
432         }
433         return value;
434     }
435 
436     private String get( InvocationProperty prop, int index )
437     {
438         return get( prop.toString(), index );
439     }
440 }