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.execution.MavenSession;
30  import org.apache.maven.model.Activation;
31  import org.apache.maven.model.ActivationOS;
32  import org.apache.maven.model.Profile;
33  import org.apache.maven.model.profile.DefaultProfileActivationContext;
34  import org.apache.maven.model.profile.ProfileActivationContext;
35  import org.apache.maven.model.profile.activation.ProfileActivator;
36  import org.codehaus.plexus.util.Os;
37  import org.codehaus.plexus.util.StringUtils;
38  
39  /**
40   * This rule checks that the OS is allowed by combinations of family, name, version and cpu architecture. The behavior
41   * is exactly the same as the Maven Os profile activation so the same values are allowed here.
42   *
43   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
44   */
45  @Named("requireOS")
46  public final class RequireOS extends AbstractStandardEnforcerRule {
47      private final ProfileActivator activator;
48  
49      private final ProfileActivationContext profileActivationContext;
50  
51      /**
52       * The OS family type desired<br />
53       * Possible values:
54       * <ul>
55       * <li>dos</li>
56       * <li>mac</li>
57       * <li>netware</li>
58       * <li>os/2</li>
59       * <li>tandem</li>
60       * <li>unix</li>
61       * <li>windows</li>
62       * <li>win9x</li>
63       * <li>z/os</li>
64       * <li>os/400</li>
65       * </ul>
66       */
67      private String family = null;
68  
69      /**
70       * The OS name desired.
71       */
72      private String name = null;
73  
74      /**
75       * The OS version desired.
76       */
77      private String version = null;
78  
79      /**
80       * The OS architecture desired.
81       */
82      private String arch = null;
83  
84      /**
85       * Display detected OS information.
86       */
87      private boolean display = false;
88  
89      /**
90       * Instantiates a new RequireOS.
91       */
92      @Inject
93      RequireOS(@Named("os") ProfileActivator activator, MavenSession session) {
94          this.activator = Objects.requireNonNull(activator);
95          this.profileActivationContext = createProfileActivationContext(session);
96      }
97  
98      private ProfileActivationContext createProfileActivationContext(MavenSession session) {
99          DefaultProfileActivationContext context = new DefaultProfileActivationContext();
100         context.setActiveProfileIds(session.getRequest().getActiveProfiles());
101         context.setInactiveProfileIds(session.getRequest().getInactiveProfiles());
102         context.setProjectDirectory(session.getCurrentProject().getBasedir());
103         context.setProjectProperties(session.getCurrentProject().getProperties());
104         context.setSystemProperties(System.getProperties());
105         context.setUserProperties(session.getUserProperties());
106         return context;
107     }
108 
109     @Override
110     public void execute() throws EnforcerRuleException {
111 
112         displayOSInfo();
113 
114         if (allParamsEmpty()) {
115             throw new EnforcerRuleError("All parameters can not be empty. "
116                     + "You must pick at least one of (family, name, version, arch), "
117                     + "you can use mvn --version to see the current OS information.");
118         }
119 
120         if (isValidFamily(this.family)) {
121             if (!isAllowed()) {
122                 String message = getMessage();
123                 if (message == null || message.isEmpty()) {
124                     // @formatter:off
125                     message = "OS Arch: "
126                             + Os.OS_ARCH + " Family: "
127                             + Os.OS_FAMILY + " Name: "
128                             + Os.OS_NAME + " Version: "
129                             + Os.OS_VERSION + " is not allowed by" + (arch != null ? " Arch=" + arch : "")
130                             + (family != null ? " Family=" + family : "")
131                             + (name != null ? " Name=" + name : "")
132                             + (version != null ? " Version=" + version : "");
133                     // @formatter:on
134                 }
135                 throw new EnforcerRuleException(message);
136             }
137         } else {
138             String validFamilies = String.join(",", Os.getValidFamilies());
139             throw new EnforcerRuleError("Invalid Family type used. Valid family types are: " + validFamilies);
140         }
141     }
142 
143     /**
144      * Log the current OS information.
145      */
146     private void displayOSInfo() {
147         String string = OSUtil.getOSInfo();
148 
149         if (!display) {
150             getLog().debug(string);
151         } else {
152             getLog().info(string);
153         }
154     }
155 
156     /**
157      * Helper method to determine if the current OS is allowed based on the injected values for family, name, version
158      * and arch.
159      *
160      * @return true if the version is allowed.
161      */
162     public boolean isAllowed() {
163         // empty lambda as problems collector
164         return activator.isActive(createProfile(), profileActivationContext, (req -> {}));
165     }
166 
167     /**
168      * Helper method to check that at least one of family, name, version or arch is set.
169      *
170      * @return true if all parameters are empty.
171      */
172     public boolean allParamsEmpty() {
173         return (family == null || family.isEmpty())
174                 && (arch == null || arch.isEmpty())
175                 && (name == null || name.isEmpty())
176                 && (version == null || version.isEmpty());
177     }
178 
179     /**
180      * Creates a Profile object that contains the activation information.
181      *
182      * @return a properly populated profile to be used for OS validation.
183      */
184     private Profile createProfile() {
185         Profile profile = new Profile();
186         profile.setActivation(createActivation());
187         return profile;
188     }
189 
190     /**
191      * Creates an Activation object that contains the ActivationOS information.
192      *
193      * @return a properly populated Activation object.
194      */
195     private Activation createActivation() {
196         Activation activation = new Activation();
197         activation.setActiveByDefault(false);
198         activation.setOs(createOsBean());
199         return activation;
200     }
201 
202     /**
203      * Creates an ActivationOS object containing family, name, version and arch.
204      *
205      * @return a properly populated ActivationOS object.
206      */
207     private ActivationOS createOsBean() {
208         ActivationOS os = new ActivationOS();
209 
210         os.setArch(arch);
211         os.setFamily(family);
212         os.setName(name);
213         os.setVersion(version);
214 
215         return os;
216     }
217 
218     /**
219      * Helper method to check if the given family is in the following list:
220      * <ul>
221      * <li>dos</li>
222      * <li>mac</li>
223      * <li>netware</li>
224      * <li>os/2</li>
225      * <li>tandem</li>
226      * <li>unix</li>
227      * <li>windows</li>
228      * <li>win9x</li>
229      * <li>z/os</li>
230      * <li>os/400</li>
231      * </ul>
232      * Note: '!' is allowed at the beginning of the string and still considered valid.
233      *
234      * @param theFamily the family to check.
235      * @return true if one of the valid families.
236      */
237     public boolean isValidFamily(String theFamily) {
238 
239         // in case they are checking !family
240         theFamily = StringUtils.stripStart(theFamily, "!");
241 
242         return (theFamily == null || theFamily.isEmpty())
243                 || Os.getValidFamilies().contains(theFamily);
244     }
245 
246     /**
247      * Sets the arch.
248      *
249      * @param theArch the arch to set
250      */
251     public void setArch(String theArch) {
252         this.arch = theArch;
253     }
254 
255     /**
256      * Sets the family.
257      *
258      * @param theFamily the family to set
259      */
260     public void setFamily(String theFamily) {
261         this.family = theFamily;
262     }
263 
264     /**
265      * Sets the name.
266      *
267      * @param theName the name to set
268      */
269     public void setName(String theName) {
270         this.name = theName;
271     }
272 
273     /**
274      * Sets the version.
275      *
276      * @param theVersion the version to set
277      */
278     public void setVersion(String theVersion) {
279         this.version = theVersion;
280     }
281 
282     /**
283      * @param display The value for the display.
284      */
285     public void setDisplay(boolean display) {
286         this.display = display;
287     }
288 
289     @Override
290     public String getCacheId() {
291         // return the hashcodes of all the parameters
292         StringBuilder b = new StringBuilder();
293         if (version != null && !version.isEmpty()) {
294             b.append(version.hashCode());
295         }
296         if (name != null && !name.isEmpty()) {
297             b.append(name.hashCode());
298         }
299         if (arch != null && !arch.isEmpty()) {
300             b.append(arch.hashCode());
301         }
302         if (family != null && !family.isEmpty()) {
303             b.append(family.hashCode());
304         }
305         return b.toString();
306     }
307 
308     @Override
309     public String toString() {
310         return String.format(
311                 "RequireOS[message=%s, arch=%s, family=%s, name=%s, version=%s, display=%b]",
312                 getMessage(), arch, family, name, version, display);
313     }
314 }