View Javadoc
1   package org.apache.maven.plugins.enforcer;
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.util.Iterator;
23  
24  import org.apache.maven.enforcer.rule.api.EnforcerRule;
25  import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
26  import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
27  import org.apache.maven.model.Activation;
28  import org.apache.maven.model.ActivationOS;
29  import org.apache.maven.model.Profile;
30  import org.apache.maven.plugin.logging.Log;
31  import org.apache.maven.model.profile.activation.ProfileActivator;
32  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
33  import org.codehaus.plexus.util.Os;
34  import org.codehaus.plexus.util.StringUtils;
35  
36  /**
37   * This rule checks that the OS is allowed by combinations of family, name, version and cpu architecture. The behavior
38   * is exactly the same as the Maven Os profile activation so the same values are allowed here.
39   *
40   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
41   */
42  public class RequireOS
43      extends AbstractStandardEnforcerRule
44  {
45      private ProfileActivator activator;
46  
47      /**
48       * The OS family type desired<br />
49       * Possible values:
50       * <ul>
51       * <li>dos</li>
52       * <li>mac</li>
53       * <li>netware</li>
54       * <li>os/2</li>
55       * <li>tandem</li>
56       * <li>unix</li>
57       * <li>windows</li>
58       * <li>win9x</li>
59       * <li>z/os</li>
60       * <li>os/400</li>
61       * </ul>
62       * 
63       * @see {@link #setFamily(String)}
64       * @see {@link #getFamily()}
65       */
66      private String family = null;
67  
68      /**
69       * The OS name desired.
70       *
71       * @see {@link #setName(String)}
72       * @see {@link #getName()}
73       */
74      private String name = null;
75  
76      /**
77       * The OS version desired.
78       * 
79       * @see {@link #setVersion(String)}
80       * @see {@link #getVersion()}
81       */
82      private String version = null;
83  
84      /**
85       * The OS architecture desired.
86       * 
87       * @see {@link #setArch(String)}
88       * @see {@link #getArch()}
89       */
90      private String arch = null;
91  
92      /**
93       * Display detected OS information.
94       * 
95       * @see {@link #setDisplay(boolean)}
96       * @see {@link #isDisplay()}
97       */
98      private boolean display = false;
99  
100     /**
101      * Instantiates a new RequireOS.
102      */
103     public RequireOS()
104     {
105 
106     }
107     
108     // For testing
109     RequireOS( ProfileActivator activator ) 
110     {
111         this.activator = activator;
112     }
113     
114 
115     @Override
116     public void execute( EnforcerRuleHelper helper )
117         throws EnforcerRuleException
118     {
119 
120         displayOSInfo( helper.getLog(), display );
121 
122         if ( allParamsEmpty() )
123         {
124             throw new EnforcerRuleException( "All parameters can not be empty. "
125                 + "You must pick at least one of (family, name, version, arch) "
126                 + "or use -Denforcer.os.display=true to see the current OS information." );
127         }
128         
129         try
130         {
131             activator = helper.getComponent( ProfileActivator.class, "os" );
132         }
133         catch ( ComponentLookupException e )
134         {
135             throw new EnforcerRuleException( e.getMessage() );
136         }
137 
138         if ( isValidFamily( this.family ) )
139         {
140             if ( !isAllowed() )
141             {
142                 String message = getMessage();
143                 if ( StringUtils.isEmpty( message ) )
144                 {
145                     //@formatter:off
146                     message =
147                         ( "OS Arch: " 
148                             + Os.OS_ARCH + " Family: " 
149                             + Os.OS_FAMILY + " Name: " 
150                             + Os.OS_NAME + " Version: "
151                             + Os.OS_VERSION + " is not allowed by" + ( arch != null ? " Arch=" + arch : "" )
152                             + ( family != null ? " Family=" + family : "" ) 
153                             + ( name != null ? " Name=" + name : "" ) 
154                             + ( version != null ? " Version=" + version : "" ) );
155                     //@formatter:on
156                 }
157                 throw new EnforcerRuleException( message );
158             }
159         }
160         else
161         {
162             final int minimumBufferSize = 50;
163             StringBuilder buffer = new StringBuilder( minimumBufferSize );
164             Iterator<?> iter = Os.getValidFamilies().iterator();
165             while ( iter.hasNext() )
166             {
167                 buffer.append( iter.next() );
168                 buffer.append( ", " );
169             }
170             String help = StringUtils.stripEnd( buffer.toString().trim(), "." );
171             throw new EnforcerRuleException( "Invalid Family type used. Valid family types are: " + help );
172         }
173     }
174 
175     /**
176      * Log the current OS information.
177      *
178      * @param log the log
179      * @param info the info
180      */
181     public void displayOSInfo( Log log, boolean info )
182     {
183         String string =
184             "OS Info: Arch: " + Os.OS_ARCH + " Family: " + Os.OS_FAMILY + " Name: " + Os.OS_NAME + " Version: "
185                 + Os.OS_VERSION;
186 
187         if ( !info )
188         {
189             log.debug( string );
190         }
191         else
192         {
193             log.info( string );
194         }
195     }
196 
197     /**
198      * Helper method to determine if the current OS is allowed based on the injected values for family, name, version
199      * and arch.
200      *
201      * @return true if the version is allowed.
202      */
203     public boolean isAllowed()
204     {
205         return activator.isActive( createProfile(), null, null );
206     }
207 
208     /**
209      * Helper method to check that at least one of family, name, version or arch is set.
210      *
211      * @return true if all parameters are empty.
212      */
213     public boolean allParamsEmpty()
214     {
215         // CHECKSTYLE_OFF: LineLength
216         return ( StringUtils.isEmpty( family ) && StringUtils.isEmpty( arch ) && StringUtils.isEmpty( name ) && StringUtils.isEmpty( version ) );
217         // CHECKSTYLE_ON: LineLength
218     }
219 
220     /**
221      * Creates a Profile object that contains the activation information.
222      *
223      * @return a properly populated profile to be used for OS validation.
224      */
225     private Profile createProfile()
226     {
227         Profile profile = new Profile();
228         profile.setActivation( createActivation() );
229         return profile;
230     }
231 
232     /**
233      * Creates an Activation object that contains the ActivationOS information.
234      *
235      * @return a properly populated Activation object.
236      */
237     private Activation createActivation()
238     {
239         Activation activation = new Activation();
240         activation.setActiveByDefault( false );
241         activation.setOs( createOsBean() );
242         return activation;
243     }
244 
245     /**
246      * Creates an ActivationOS object containing family, name, version and arch.
247      *
248      * @return a properly populated ActivationOS object.
249      */
250     private ActivationOS createOsBean()
251     {
252         ActivationOS os = new ActivationOS();
253 
254         os.setArch( arch );
255         os.setFamily( family );
256         os.setName( name );
257         os.setVersion( version );
258 
259         return os;
260     }
261 
262     /**
263      * Helper method to check if the given family is in the following list:
264      * <ul>
265      * <li>dos</li>
266      * <li>mac</li>
267      * <li>netware</li>
268      * <li>os/2</li>
269      * <li>tandem</li>
270      * <li>unix</li>
271      * <li>windows</li>
272      * <li>win9x</li>
273      * <li>z/os</li>
274      * <li>os/400</li>
275      * </ul>
276      * Note: '!' is allowed at the beginning of the string and still considered valid.
277      *
278      * @param theFamily the family to check.
279      * @return true if one of the valid families.
280      */
281     public boolean isValidFamily( String theFamily )
282     {
283 
284         // in case they are checking !family
285         theFamily = StringUtils.stripStart( theFamily, "!" );
286 
287         return ( StringUtils.isEmpty( theFamily ) || Os.getValidFamilies().contains( theFamily ) );
288     }
289 
290     /**
291      * Gets the arch.
292      *
293      * @return the arch
294      */
295     public String getArch()
296     {
297         return this.arch;
298     }
299 
300     /**
301      * Sets the arch.
302      *
303      * @param theArch the arch to set
304      */
305     public void setArch( String theArch )
306     {
307         this.arch = theArch;
308     }
309 
310     /**
311      * Gets the family.
312      *
313      * @return the family
314      */
315     public String getFamily()
316     {
317         return this.family;
318     }
319 
320     /**
321      * Sets the family.
322      *
323      * @param theFamily the family to set
324      */
325     public void setFamily( String theFamily )
326     {
327         this.family = theFamily;
328     }
329 
330     /**
331      * Gets the name.
332      *
333      * @return the name
334      */
335     public String getName()
336     {
337         return this.name;
338     }
339 
340     /**
341      * Sets the name.
342      *
343      * @param theName the name to set
344      */
345     public void setName( String theName )
346     {
347         this.name = theName;
348     }
349 
350     /**
351      * Gets the version.
352      *
353      * @return the version
354      */
355     public String getVersion()
356     {
357         return this.version;
358     }
359 
360     /**
361      * Sets the version.
362      *
363      * @param theVersion the version to set
364      */
365     public void setVersion( String theVersion )
366     {
367         this.version = theVersion;
368     }
369 
370     /**
371      * @param display The value for the display.
372      */
373     public final void setDisplay( boolean display )
374     {
375         this.display = display;
376     }
377 
378     public final boolean isDisplay()
379     {
380         return display;
381     }
382 
383     @Override
384     public String getCacheId()
385     {
386         // return the hashcodes of all the parameters
387         StringBuffer b = new StringBuffer();
388         if ( StringUtils.isNotEmpty( version ) )
389         {
390             b.append( version.hashCode() );
391         }
392         if ( StringUtils.isNotEmpty( name ) )
393         {
394             b.append( name.hashCode() );
395         }
396         if ( StringUtils.isNotEmpty( arch ) )
397         {
398             b.append( arch.hashCode() );
399         }
400         if ( StringUtils.isNotEmpty( family ) )
401         {
402             b.append( family.hashCode() );
403         }
404         return b.toString();
405     }
406 
407     @Override
408     public boolean isCacheable()
409     {
410         // the os is not going to change between projects in the same build.
411         return true;
412     }
413 
414     @Override
415     public boolean isResultValid( EnforcerRule theCachedRule )
416     {
417         // i will always return the hash of the parameters as my id. If my parameters are the same, this
418         // rule must always have the same result.
419         return true;
420     }
421 }