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