View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.enforcer.rules;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  
24  import java.util.Objects;
25  
26  import org.apache.maven.enforcer.rule.api.EnforcerRuleError;
27  import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
28  import org.apache.maven.enforcer.rules.utils.OSUtil;
29  import org.apache.maven.model.Activation;
30  import org.apache.maven.model.ActivationOS;
31  import org.apache.maven.model.Profile;
32  import org.apache.maven.model.profile.activation.ProfileActivator;
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  @Named("requireOS")
43  public final class RequireOS extends AbstractStandardEnforcerRule {
44      private final ProfileActivator activator;
45  
46      /**
47       * The OS family type desired<br />
48       * Possible values:
49       * <ul>
50       * <li>dos</li>
51       * <li>mac</li>
52       * <li>netware</li>
53       * <li>os/2</li>
54       * <li>tandem</li>
55       * <li>unix</li>
56       * <li>windows</li>
57       * <li>win9x</li>
58       * <li>z/os</li>
59       * <li>os/400</li>
60       * </ul>
61       */
62      private String family = null;
63  
64      /**
65       * The OS name desired.
66       */
67      private String name = null;
68  
69      /**
70       * The OS version desired.
71       */
72      private String version = null;
73  
74      /**
75       * The OS architecture desired.
76       */
77      private String arch = null;
78  
79      /**
80       * Display detected OS information.
81       */
82      private boolean display = false;
83  
84      /**
85       * Instantiates a new RequireOS.
86       */
87      @Inject
88      RequireOS(@Named("os") ProfileActivator activator) {
89          this.activator = Objects.requireNonNull(activator);
90      }
91  
92      @Override
93      public void execute() throws EnforcerRuleException {
94  
95          displayOSInfo();
96  
97          if (allParamsEmpty()) {
98              throw new EnforcerRuleError("All parameters can not be empty. "
99                      + "You must pick at least one of (family, name, version, arch), "
100                     + "you can use mvn --version to see the current OS information.");
101         }
102 
103         if (isValidFamily(this.family)) {
104             if (!isAllowed()) {
105                 String message = getMessage();
106                 if (message == null || message.isEmpty()) {
107                     // @formatter:off
108                     message = "OS Arch: "
109                             + Os.OS_ARCH + " Family: "
110                             + Os.OS_FAMILY + " Name: "
111                             + Os.OS_NAME + " Version: "
112                             + Os.OS_VERSION + " is not allowed by" + (arch != null ? " Arch=" + arch : "")
113                             + (family != null ? " Family=" + family : "")
114                             + (name != null ? " Name=" + name : "")
115                             + (version != null ? " Version=" + version : "");
116                     // @formatter:on
117                 }
118                 throw new EnforcerRuleException(message);
119             }
120         } else {
121             String validFamilies = String.join(",", Os.getValidFamilies());
122             throw new EnforcerRuleError("Invalid Family type used. Valid family types are: " + validFamilies);
123         }
124     }
125 
126     /**
127      * Log the current OS information.
128      */
129     private void displayOSInfo() {
130         String string = OSUtil.getOSInfo();
131 
132         if (!display) {
133             getLog().debug(string);
134         } else {
135             getLog().info(string);
136         }
137     }
138 
139     /**
140      * Helper method to determine if the current OS is allowed based on the injected values for family, name, version
141      * and arch.
142      *
143      * @return true if the version is allowed.
144      */
145     public boolean isAllowed() {
146         return activator.isActive(createProfile(), null, null);
147     }
148 
149     /**
150      * Helper method to check that at least one of family, name, version or arch is set.
151      *
152      * @return true if all parameters are empty.
153      */
154     public boolean allParamsEmpty() {
155         return (family == null || family.isEmpty())
156                 && (arch == null || arch.isEmpty())
157                 && (name == null || name.isEmpty())
158                 && (version == null || version.isEmpty());
159     }
160 
161     /**
162      * Creates a Profile object that contains the activation information.
163      *
164      * @return a properly populated profile to be used for OS validation.
165      */
166     private Profile createProfile() {
167         Profile profile = new Profile();
168         profile.setActivation(createActivation());
169         return profile;
170     }
171 
172     /**
173      * Creates an Activation object that contains the ActivationOS information.
174      *
175      * @return a properly populated Activation object.
176      */
177     private Activation createActivation() {
178         Activation activation = new Activation();
179         activation.setActiveByDefault(false);
180         activation.setOs(createOsBean());
181         return activation;
182     }
183 
184     /**
185      * Creates an ActivationOS object containing family, name, version and arch.
186      *
187      * @return a properly populated ActivationOS object.
188      */
189     private ActivationOS createOsBean() {
190         ActivationOS os = new ActivationOS();
191 
192         os.setArch(arch);
193         os.setFamily(family);
194         os.setName(name);
195         os.setVersion(version);
196 
197         return os;
198     }
199 
200     /**
201      * Helper method to check if the given family is in the following list:
202      * <ul>
203      * <li>dos</li>
204      * <li>mac</li>
205      * <li>netware</li>
206      * <li>os/2</li>
207      * <li>tandem</li>
208      * <li>unix</li>
209      * <li>windows</li>
210      * <li>win9x</li>
211      * <li>z/os</li>
212      * <li>os/400</li>
213      * </ul>
214      * Note: '!' is allowed at the beginning of the string and still considered valid.
215      *
216      * @param theFamily the family to check.
217      * @return true if one of the valid families.
218      */
219     public boolean isValidFamily(String theFamily) {
220 
221         // in case they are checking !family
222         theFamily = StringUtils.stripStart(theFamily, "!");
223 
224         return (theFamily == null || theFamily.isEmpty())
225                 || Os.getValidFamilies().contains(theFamily);
226     }
227 
228     /**
229      * Sets the arch.
230      *
231      * @param theArch the arch to set
232      */
233     public void setArch(String theArch) {
234         this.arch = theArch;
235     }
236 
237     /**
238      * Sets the family.
239      *
240      * @param theFamily the family to set
241      */
242     public void setFamily(String theFamily) {
243         this.family = theFamily;
244     }
245 
246     /**
247      * Sets the name.
248      *
249      * @param theName the name to set
250      */
251     public void setName(String theName) {
252         this.name = theName;
253     }
254 
255     /**
256      * Sets the version.
257      *
258      * @param theVersion the version to set
259      */
260     public void setVersion(String theVersion) {
261         this.version = theVersion;
262     }
263 
264     /**
265      * @param display The value for the display.
266      */
267     public void setDisplay(boolean display) {
268         this.display = display;
269     }
270 
271     @Override
272     public String getCacheId() {
273         // return the hashcodes of all the parameters
274         StringBuilder b = new StringBuilder();
275         if (version != null && !version.isEmpty()) {
276             b.append(version.hashCode());
277         }
278         if (name != null && !name.isEmpty()) {
279             b.append(name.hashCode());
280         }
281         if (arch != null && !arch.isEmpty()) {
282             b.append(arch.hashCode());
283         }
284         if (family != null && !family.isEmpty()) {
285             b.append(family.hashCode());
286         }
287         return b.toString();
288     }
289 
290     @Override
291     public String toString() {
292         return String.format(
293                 "RequireOS[message=%s, arch=%s, family=%s, name=%s, version=%s, display=%b]",
294                 getMessage(), arch, family, name, version, display);
295     }
296 }