001package org.apache.maven.plugins.enforcer;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.Iterator;
023
024import org.apache.maven.enforcer.rule.api.EnforcerRule;
025import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
026import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
027import org.apache.maven.model.Activation;
028import org.apache.maven.model.ActivationOS;
029import org.apache.maven.model.Profile;
030import org.apache.maven.plugin.logging.Log;
031import org.apache.maven.profiles.activation.OperatingSystemProfileActivator;
032import org.codehaus.plexus.util.Os;
033import org.codehaus.plexus.util.StringUtils;
034
035/**
036 * This rule checks that the OS is allowed by combinations of family, name, version and cpu architecture. The behavior
037 * is exactly the same as the Maven Os profile activation so the same values are allowed here.
038 *
039 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
040 * @version $Id: RequireOS.java 1496229 2013-06-24 21:43:56Z rfscholte $
041 */
042public class RequireOS
043    extends AbstractStandardEnforcerRule
044{
045
046    /**
047     * The OS family type desired<br />
048     * Possible values:
049     * <ul>
050     * <li>dos</li>
051     * <li>mac</li>
052     * <li>netware</li>
053     * <li>os/2</li>
054     * <li>tandem</li>
055     * <li>unix</li>
056     * <li>windows</li>
057     * <li>win9x</li>
058     * <li>z/os</li>
059     * <li>os/400</li>
060     * </ul>
061     * 
062     * @deprecated the visibility will be reduced to private with the next major version
063     * @see {@link #setFamily(String)}
064     * @see {@link #getFamily()}
065     */
066    public String family = null;
067
068    /** The OS name desired.
069     *
070     * @deprecated the visibility will be reduced to private with the next major version
071     * @see {@link #setName(String)}
072     * @see {@link #getName()}
073     */
074    public String name = null;
075
076    /** The OS version desired.
077     * 
078     * @deprecated the visibility will be reduced to private with the next major version
079     * @see {@link #setVersion(String)}
080     * @see {@link #getVersion()}
081     */
082    public String version = null;
083
084    /** The OS architecture desired.
085     *  
086     * @deprecated the visibility will be reduced to private with the next major version
087     * @see {@link #setArch(String)}
088     * @see {@link #getArch()} 
089     */
090    public String arch = null;
091
092    /** Display detected OS information.
093     *  
094     * @deprecated the visibility will be reduced to private with the next major version
095     * @see {@link #setDisplay(boolean)}
096     * @see {@link #isDisplay()}
097     * */
098    public boolean display = false;
099
100    /**
101     * Instantiates a new RequireOS.
102     */
103    public RequireOS()
104    {
105
106    }
107
108    /*
109     * (non-Javadoc)
110     *
111     * @see org.apache.maven.enforcer.rule.api.EnforcerRule#execute(org.apache.maven.enforcer.rule.api.EnforcerRuleHelper)
112     */
113    public void execute( EnforcerRuleHelper helper )
114        throws EnforcerRuleException
115    {
116
117        displayOSInfo( helper.getLog(), display );
118
119        if ( allParamsEmpty() )
120        {
121            throw new EnforcerRuleException( "All parameters can not be empty. You must pick at least one of (family, name, version, arch) or use -Denforcer.os.display=true to see the current OS information." );
122        }
123
124        if ( isValidFamily( this.family ) )
125        {
126            if ( !isAllowed() )
127            {
128                String message = getMessage();
129                if ( StringUtils.isEmpty( message ) )
130                {
131                    message =
132                        ( "OS Arch: " + Os.OS_ARCH + " Family: " + Os.OS_FAMILY + " Name: " + Os.OS_NAME + " Version: "
133                            + Os.OS_VERSION + " is not allowed by" + ( arch != null ? " Arch=" + arch : "" )
134                            + ( family != null ? " Family=" + family : "" ) + ( name != null ? " Name=" + name : "" ) + ( version != null ? " Version="
135                            + version
136                                        : "" ) );
137                }
138                throw new EnforcerRuleException( message );
139            }
140        }
141        else
142        {
143            StringBuilder buffer = new StringBuilder( 50 );
144            Iterator iter = Os.getValidFamilies().iterator();
145            while ( iter.hasNext() )
146            {
147                buffer.append( iter.next() );
148                buffer.append( ", " );
149            }
150            String help = StringUtils.stripEnd( buffer.toString().trim(), "." );
151            throw new EnforcerRuleException( "Invalid Family type used. Valid family types are: " + help );
152        }
153    }
154
155    /**
156     * Log the current OS information.
157     *
158     * @param log the log
159     * @param info the info
160     */
161    public void displayOSInfo( Log log, boolean info )
162    {
163        String string =
164            "OS Info: Arch: " + Os.OS_ARCH + " Family: " + Os.OS_FAMILY + " Name: " + Os.OS_NAME + " Version: "
165                + Os.OS_VERSION;
166
167        if ( !info )
168        {
169            log.debug( string );
170        }
171        else
172        {
173            log.info( string );
174        }
175    }
176
177    /**
178     * Helper method to determine if the current OS is allowed based on the injected values for family, name, version
179     * and arch.
180     *
181     * @return true if the version is allowed.
182     */
183    public boolean isAllowed()
184    {
185        OperatingSystemProfileActivator activator = new OperatingSystemProfileActivator();
186
187        return activator.isActive( createProfile() );
188    }
189
190    /**
191     * Helper method to check that at least one of family, name, version or arch is set.
192     *
193     * @return true if all parameters are empty.
194     */
195    public boolean allParamsEmpty()
196    {
197        return ( StringUtils.isEmpty( family ) && StringUtils.isEmpty( arch ) && StringUtils.isEmpty( name ) && StringUtils.isEmpty( version ) );
198
199    }
200
201    /**
202     * Creates a Profile object that contains the activation information.
203     *
204     * @return a properly populated profile to be used for OS validation.
205     */
206    private Profile createProfile()
207    {
208        Profile profile = new Profile();
209        profile.setActivation( createActivation() );
210        return profile;
211    }
212
213    /**
214     * Creates an Activation object that contains the ActivationOS information.
215     *
216     * @return a properly populated Activation object.
217     */
218    private Activation createActivation()
219    {
220        Activation activation = new Activation();
221        activation.setActiveByDefault( false );
222        activation.setOs( createOsBean() );
223        return activation;
224    }
225
226    /**
227     * Creates an ActivationOS object containing family, name, version and arch.
228     *
229     * @return a properly populated ActivationOS object.
230     */
231    private ActivationOS createOsBean()
232    {
233        ActivationOS os = new ActivationOS();
234
235        os.setArch( arch );
236        os.setFamily( family );
237        os.setName( name );
238        os.setVersion( version );
239
240        return os;
241    }
242
243    /**
244     * Helper method to check if the given family is in the following list:
245     * <ul>
246     * <li>dos</li>
247     * <li>mac</li>
248     * <li>netware</li>
249     * <li>os/2</li>
250     * <li>tandem</li>
251     * <li>unix</li>
252     * <li>windows</li>
253     * <li>win9x</li>
254     * <li>z/os</li>
255     * <li>os/400</li>
256     * </ul>
257     * Note: '!' is allowed at the beginning of the string and still considered valid.
258     *
259     * @param theFamily the family to check.
260     * @return true if one of the valid families.
261     */
262    public boolean isValidFamily( String theFamily )
263    {
264
265        // in case they are checking !family
266        theFamily = StringUtils.stripStart( theFamily, "!" );
267
268        return ( StringUtils.isEmpty( theFamily ) || Os.getValidFamilies().contains( theFamily ) );
269    }
270
271    /**
272     * Gets the arch.
273     *
274     * @return the arch
275     */
276    public String getArch()
277    {
278        return this.arch;
279    }
280
281    /**
282     * Sets the arch.
283     *
284     * @param theArch the arch to set
285     */
286    public void setArch( String theArch )
287    {
288        this.arch = theArch;
289    }
290
291    /**
292     * Gets the family.
293     *
294     * @return the family
295     */
296    public String getFamily()
297    {
298        return this.family;
299    }
300
301    /**
302     * Sets the family.
303     *
304     * @param theFamily the family to set
305     */
306    public void setFamily( String theFamily )
307    {
308        this.family = theFamily;
309    }
310
311    /**
312     * Gets the name.
313     *
314     * @return the name
315     */
316    public String getName()
317    {
318        return this.name;
319    }
320
321    /**
322     * Sets the name.
323     *
324     * @param theName the name to set
325     */
326    public void setName( String theName )
327    {
328        this.name = theName;
329    }
330
331    /**
332     * Gets the version.
333     *
334     * @return the version
335     */
336    public String getVersion()
337    {
338        return this.version;
339    }
340
341    /**
342     * Sets the version.
343     *
344     * @param theVersion the version to set
345     */
346    public void setVersion( String theVersion )
347    {
348        this.version = theVersion;
349    }
350    
351    public final void setDisplay( boolean display )
352    {
353        this.display = display;
354    }
355    
356    public final boolean isDisplay()
357    {
358        return display;
359    }
360
361    /*
362     * (non-Javadoc)
363     *
364     * @see org.apache.maven.enforcer.rule.api.EnforcerRule#getCacheId()
365     */
366    public String getCacheId()
367    {
368        // return the hashcodes of all the parameters
369        StringBuffer b = new StringBuffer();
370        if ( StringUtils.isNotEmpty( version ) )
371        {
372            b.append( version.hashCode() );
373        }
374        if ( StringUtils.isNotEmpty( name ) )
375        {
376            b.append( name.hashCode() );
377        }
378        if ( StringUtils.isNotEmpty( arch ) )
379        {
380            b.append( arch.hashCode() );
381        }
382        if ( StringUtils.isNotEmpty( family ) )
383        {
384            b.append( family.hashCode() );
385        }
386        return b.toString();
387    }
388
389    /*
390     * (non-Javadoc)
391     *
392     * @see org.apache.maven.enforcer.rule.api.EnforcerRule#isCacheable()
393     */
394    public boolean isCacheable()
395    {
396        // the os is not going to change between projects in the same build.
397        return true;
398    }
399
400    /*
401     * (non-Javadoc)
402     *
403     * @see org.apache.maven.enforcer.rule.api.EnforcerRule#isResultValid(org.apache.maven.enforcer.rule.api.EnforcerRule)
404     */
405    public boolean isResultValid( EnforcerRule theCachedRule )
406    {
407        // i will always return the hash of the parameters as my id. If my parameters are the same, this
408        // rule must always have the same result.
409        return true;
410    }
411}