001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.enforcer.rules; 020 021import javax.inject.Inject; 022import javax.inject.Named; 023 024import java.util.Objects; 025 026import org.apache.maven.enforcer.rule.api.EnforcerRuleError; 027import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 028import org.apache.maven.enforcer.rules.utils.OSUtil; 029import org.apache.maven.execution.MavenSession; 030import org.apache.maven.model.Activation; 031import org.apache.maven.model.ActivationOS; 032import org.apache.maven.model.Profile; 033import org.apache.maven.model.profile.DefaultProfileActivationContext; 034import org.apache.maven.model.profile.ProfileActivationContext; 035import org.apache.maven.model.profile.activation.ProfileActivator; 036import org.codehaus.plexus.util.Os; 037import org.codehaus.plexus.util.StringUtils; 038 039/** 040 * This rule checks that the OS is allowed by combinations of family, name, version and cpu architecture. The behavior 041 * is exactly the same as the Maven Os profile activation so the same values are allowed here. 042 * 043 * @author <a href="mailto:brianf@apache.org">Brian Fox</a> 044 */ 045@Named("requireOS") 046public final class RequireOS extends AbstractStandardEnforcerRule { 047 private final ProfileActivator activator; 048 049 private final ProfileActivationContext profileActivationContext; 050 051 /** 052 * The OS family type desired<br /> 053 * Possible values: 054 * <ul> 055 * <li>dos</li> 056 * <li>mac</li> 057 * <li>netware</li> 058 * <li>os/2</li> 059 * <li>tandem</li> 060 * <li>unix</li> 061 * <li>windows</li> 062 * <li>win9x</li> 063 * <li>z/os</li> 064 * <li>os/400</li> 065 * </ul> 066 */ 067 private String family = null; 068 069 /** 070 * The OS name desired. 071 */ 072 private String name = null; 073 074 /** 075 * The OS version desired. 076 */ 077 private String version = null; 078 079 /** 080 * The OS architecture desired. 081 */ 082 private String arch = null; 083 084 /** 085 * Display detected OS information. 086 */ 087 private boolean display = false; 088 089 /** 090 * Instantiates a new RequireOS. 091 */ 092 @Inject 093 RequireOS(@Named("os") ProfileActivator activator, MavenSession session) { 094 this.activator = Objects.requireNonNull(activator); 095 this.profileActivationContext = createProfileActivationContext(session); 096 } 097 098 private ProfileActivationContext createProfileActivationContext(MavenSession session) { 099 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}