1 /* 2 * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.nio.file.attribute; 27 28 import java.util.*; 29 30 /** 31 * An entry in an access control list (ACL). 32 * 33 * <p> The ACL entry represented by this class is based on the ACL model 34 * specified in <a href="http://www.ietf.org/rfc/rfc3530.txt"><i>RFC 3530: 35 * Network File System (NFS) version 4 Protocol</i></a>. Each entry has four 36 * components as follows: 37 * 38 * <ol> 39 * <li><p> The {@link #type() type} component determines if the entry 40 * grants or denies access. </p></li> 41 * 42 * <li><p> The {@link #principal() principal} component, sometimes called the 43 * "who" component, is a {@link UserPrincipal} corresponding to the identity 44 * that the entry grants or denies access 45 * </p></li> 46 * 47 * <li><p> The {@link #permissions permissions} component is a set of 48 * {@link AclEntryPermission permissions} 49 * </p></li> 50 * 51 * <li><p> The {@link #flags flags} component is a set of {@link AclEntryFlag 52 * flags} to indicate how entries are inherited and propagated </p></li> 53 * </ol> 54 * 55 * <p> ACL entries are created using an associated {@link Builder} object by 56 * invoking its {@link Builder#build build} method. 57 * 58 * <p> ACL entries are immutable and are safe for use by multiple concurrent 59 * threads. 60 * 61 * @since 1.7 62 */ 63 64 public final class AclEntry { 65 66 private final AclEntryType type; 67 private final UserPrincipal who; 68 private final Set<AclEntryPermission> perms; 69 private final Set<AclEntryFlag> flags; 70 71 // cached hash code 72 private volatile int hash; 73 74 // private constructor AclEntry(AclEntryType type, UserPrincipal who, Set<AclEntryPermission> perms, Set<AclEntryFlag> flags)75 private AclEntry(AclEntryType type, 76 UserPrincipal who, 77 Set<AclEntryPermission> perms, 78 Set<AclEntryFlag> flags) 79 { 80 this.type = type; 81 this.who = who; 82 this.perms = perms; 83 this.flags = flags; 84 } 85 86 /** 87 * A builder of {@link AclEntry} objects. 88 * 89 * <p> A {@code Builder} object is obtained by invoking one of the {@link 90 * AclEntry#newBuilder newBuilder} methods defined by the {@code AclEntry} 91 * class. 92 * 93 * <p> Builder objects are mutable and are not safe for use by multiple 94 * concurrent threads without appropriate synchronization. 95 * 96 * @since 1.7 97 */ 98 public static final class Builder { 99 private AclEntryType type; 100 private UserPrincipal who; 101 private Set<AclEntryPermission> perms; 102 private Set<AclEntryFlag> flags; 103 Builder(AclEntryType type, UserPrincipal who, Set<AclEntryPermission> perms, Set<AclEntryFlag> flags)104 private Builder(AclEntryType type, 105 UserPrincipal who, 106 Set<AclEntryPermission> perms, 107 Set<AclEntryFlag> flags) 108 { 109 assert perms != null && flags != null; 110 this.type = type; 111 this.who = who; 112 this.perms = perms; 113 this.flags = flags; 114 } 115 116 /** 117 * Constructs an {@link AclEntry} from the components of this builder. 118 * The type and who components are required to have been set in order 119 * to construct an {@code AclEntry}. 120 * 121 * @return a new ACL entry 122 * 123 * @throws IllegalStateException 124 * if the type or who component have not been set 125 */ build()126 public AclEntry build() { 127 if (type == null) 128 throw new IllegalStateException("Missing type component"); 129 if (who == null) 130 throw new IllegalStateException("Missing who component"); 131 return new AclEntry(type, who, perms, flags); 132 } 133 134 /** 135 * Sets the type component of this builder. 136 * 137 * @param type the component type 138 * @return this builder 139 */ setType(AclEntryType type)140 public Builder setType(AclEntryType type) { 141 if (type == null) 142 throw new NullPointerException(); 143 this.type = type; 144 return this; 145 } 146 147 /** 148 * Sets the principal component of this builder. 149 * 150 * @param who the principal component 151 * @return this builder 152 */ setPrincipal(UserPrincipal who)153 public Builder setPrincipal(UserPrincipal who) { 154 if (who == null) 155 throw new NullPointerException(); 156 this.who = who; 157 return this; 158 } 159 160 // check set only contains elements of the given type checkSet(Set<?> set, Class<?> type)161 private static void checkSet(Set<?> set, Class<?> type) { 162 for (Object e: set) { 163 if (e == null) 164 throw new NullPointerException(); 165 type.cast(e); 166 } 167 } 168 169 /** 170 * Sets the permissions component of this builder. On return, the 171 * permissions component of this builder is a copy of the given set. 172 * 173 * @param perms the permissions component 174 * @return this builder 175 * 176 * @throws ClassCastException 177 * if the set contains elements that are not of type {@code 178 * AclEntryPermission} 179 */ setPermissions(Set<AclEntryPermission> perms)180 public Builder setPermissions(Set<AclEntryPermission> perms) { 181 if (perms.isEmpty()) { 182 // EnumSet.copyOf does not allow empty set 183 perms = Collections.emptySet(); 184 } else { 185 // copy and check for erroneous elements 186 perms = EnumSet.copyOf(perms); 187 checkSet(perms, AclEntryPermission.class); 188 } 189 190 this.perms = perms; 191 return this; 192 } 193 194 /** 195 * Sets the permissions component of this builder. On return, the 196 * permissions component of this builder is a copy of the permissions in 197 * the given array. 198 * 199 * @param perms the permissions component 200 * @return this builder 201 */ setPermissions(AclEntryPermission... perms)202 public Builder setPermissions(AclEntryPermission... perms) { 203 Set<AclEntryPermission> set = EnumSet.noneOf(AclEntryPermission.class); 204 // copy and check for null elements 205 for (AclEntryPermission p: perms) { 206 if (p == null) 207 throw new NullPointerException(); 208 set.add(p); 209 } 210 this.perms = set; 211 return this; 212 } 213 214 /** 215 * Sets the flags component of this builder. On return, the flags 216 * component of this builder is a copy of the given set. 217 * 218 * @param flags the flags component 219 * @return this builder 220 * 221 * @throws ClassCastException 222 * if the set contains elements that are not of type {@code 223 * AclEntryFlag} 224 */ setFlags(Set<AclEntryFlag> flags)225 public Builder setFlags(Set<AclEntryFlag> flags) { 226 if (flags.isEmpty()) { 227 // EnumSet.copyOf does not allow empty set 228 flags = Collections.emptySet(); 229 } else { 230 // copy and check for erroneous elements 231 flags = EnumSet.copyOf(flags); 232 checkSet(flags, AclEntryFlag.class); 233 } 234 235 this.flags = flags; 236 return this; 237 } 238 239 /** 240 * Sets the flags component of this builder. On return, the flags 241 * component of this builder is a copy of the flags in the given 242 * array. 243 * 244 * @param flags the flags component 245 * @return this builder 246 */ setFlags(AclEntryFlag... flags)247 public Builder setFlags(AclEntryFlag... flags) { 248 Set<AclEntryFlag> set = EnumSet.noneOf(AclEntryFlag.class); 249 // copy and check for null elements 250 for (AclEntryFlag f: flags) { 251 if (f == null) 252 throw new NullPointerException(); 253 set.add(f); 254 } 255 this.flags = set; 256 return this; 257 } 258 } 259 260 /** 261 * Constructs a new builder. The initial value of the type and who 262 * components is {@code null}. The initial value of the permissions and 263 * flags components is the empty set. 264 * 265 * @return a new builder 266 */ newBuilder()267 public static Builder newBuilder() { 268 Set<AclEntryPermission> perms = Collections.emptySet(); 269 Set<AclEntryFlag> flags = Collections.emptySet(); 270 return new Builder(null, null, perms, flags); 271 } 272 273 /** 274 * Constructs a new builder with the components of an existing ACL entry. 275 * 276 * @param entry an ACL entry 277 * @return a new builder 278 */ newBuilder(AclEntry entry)279 public static Builder newBuilder(AclEntry entry) { 280 return new Builder(entry.type, entry.who, entry.perms, entry.flags); 281 } 282 283 /** 284 * Returns the ACL entry type. 285 * 286 * @return the ACL entry type 287 */ type()288 public AclEntryType type() { 289 return type; 290 } 291 292 /** 293 * Returns the principal component. 294 * 295 * @return the principal component 296 */ principal()297 public UserPrincipal principal() { 298 return who; 299 } 300 301 /** 302 * Returns a copy of the permissions component. 303 * 304 * <p> The returned set is a modifiable copy of the permissions. 305 * 306 * @return the permissions component 307 */ permissions()308 public Set<AclEntryPermission> permissions() { 309 return new HashSet<AclEntryPermission>(perms); 310 } 311 312 /** 313 * Returns a copy of the flags component. 314 * 315 * <p> The returned set is a modifiable copy of the flags. 316 * 317 * @return the flags component 318 */ flags()319 public Set<AclEntryFlag> flags() { 320 return new HashSet<AclEntryFlag>(flags); 321 } 322 323 /** 324 * Compares the specified object with this ACL entry for equality. 325 * 326 * <p> If the given object is not an {@code AclEntry} then this method 327 * immediately returns {@code false}. 328 * 329 * <p> For two ACL entries to be considered equals requires that they are 330 * both the same type, their who components are equal, their permissions 331 * components are equal, and their flags components are equal. 332 * 333 * <p> This method satisfies the general contract of the {@link 334 * java.lang.Object#equals(Object) Object.equals} method. </p> 335 * 336 * @param ob the object to which this object is to be compared 337 * 338 * @return {@code true} if, and only if, the given object is an AclEntry that 339 * is identical to this AclEntry 340 */ 341 @Override equals(Object ob)342 public boolean equals(Object ob) { 343 if (ob == this) 344 return true; 345 if (ob == null || !(ob instanceof AclEntry)) 346 return false; 347 AclEntry other = (AclEntry)ob; 348 if (this.type != other.type) 349 return false; 350 if (!this.who.equals(other.who)) 351 return false; 352 if (!this.perms.equals(other.perms)) 353 return false; 354 if (!this.flags.equals(other.flags)) 355 return false; 356 return true; 357 } 358 hash(int h, Object o)359 private static int hash(int h, Object o) { 360 return h * 127 + o.hashCode(); 361 } 362 363 /** 364 * Returns the hash-code value for this ACL entry. 365 * 366 * <p> This method satisfies the general contract of the {@link 367 * Object#hashCode} method. 368 */ 369 @Override hashCode()370 public int hashCode() { 371 // return cached hash if available 372 if (hash != 0) 373 return hash; 374 int h = type.hashCode(); 375 h = hash(h, who); 376 h = hash(h, perms); 377 h = hash(h, flags); 378 hash = h; 379 return hash; 380 } 381 382 /** 383 * Returns the string representation of this ACL entry. 384 * 385 * @return the string representation of this entry 386 */ 387 @Override toString()388 public String toString() { 389 StringBuilder sb = new StringBuilder(); 390 391 // who 392 sb.append(who.getName()); 393 sb.append(':'); 394 395 // permissions 396 for (AclEntryPermission perm: perms) { 397 sb.append(perm.name()); 398 sb.append('/'); 399 } 400 sb.setLength(sb.length()-1); // drop final slash 401 sb.append(':'); 402 403 // flags 404 if (!flags.isEmpty()) { 405 for (AclEntryFlag flag: flags) { 406 sb.append(flag.name()); 407 sb.append('/'); 408 } 409 sb.setLength(sb.length()-1); // drop final slash 410 sb.append(':'); 411 } 412 413 // type 414 sb.append(type.name()); 415 return sb.toString(); 416 } 417 } 418