31 import java.util.Map; |
31 import java.util.Map; |
32 import java.util.HashMap; |
32 import java.util.HashMap; |
33 import java.util.List; |
33 import java.util.List; |
34 import java.util.Iterator; |
34 import java.util.Iterator; |
35 import java.util.Collections; |
35 import java.util.Collections; |
|
36 import java.util.concurrent.ConcurrentHashMap; |
36 import java.io.Serializable; |
37 import java.io.Serializable; |
37 import java.io.ObjectStreamField; |
38 import java.io.ObjectStreamField; |
38 import java.io.ObjectOutputStream; |
39 import java.io.ObjectOutputStream; |
39 import java.io.ObjectInputStream; |
40 import java.io.ObjectInputStream; |
40 import java.io.IOException; |
41 import java.io.IOException; |
83 { |
84 { |
84 /** |
85 /** |
85 * Key is permissions Class, value is PermissionCollection for that class. |
86 * Key is permissions Class, value is PermissionCollection for that class. |
86 * Not serialized; see serialization section at end of class. |
87 * Not serialized; see serialization section at end of class. |
87 */ |
88 */ |
88 private transient Map<Class<?>, PermissionCollection> permsMap; |
89 private transient ConcurrentHashMap<Class<?>, PermissionCollection> permsMap; |
89 |
90 |
90 // optimization. keep track of whether unresolved permissions need to be |
91 // optimization. keep track of whether unresolved permissions need to be |
91 // checked |
92 // checked |
92 private transient boolean hasUnresolved = false; |
93 private transient boolean hasUnresolved = false; |
93 |
94 |
97 |
98 |
98 /** |
99 /** |
99 * Creates a new Permissions object containing no PermissionCollections. |
100 * Creates a new Permissions object containing no PermissionCollections. |
100 */ |
101 */ |
101 public Permissions() { |
102 public Permissions() { |
102 permsMap = new HashMap<>(11); |
103 permsMap = new ConcurrentHashMap<>(11); |
103 allPermission = null; |
104 allPermission = null; |
104 } |
105 } |
105 |
106 |
106 /** |
107 /** |
107 * Adds a permission object to the PermissionCollection for the class the |
108 * Adds a permission object to the PermissionCollection for the class the |
118 * @exception SecurityException if this Permissions object is |
119 * @exception SecurityException if this Permissions object is |
119 * marked as readonly. |
120 * marked as readonly. |
120 * |
121 * |
121 * @see PermissionCollection#isReadOnly() |
122 * @see PermissionCollection#isReadOnly() |
122 */ |
123 */ |
123 |
124 @Override |
124 public void add(Permission permission) { |
125 public void add(Permission permission) { |
125 if (isReadOnly()) |
126 if (isReadOnly()) |
126 throw new SecurityException( |
127 throw new SecurityException( |
127 "attempt to add a Permission to a readonly Permissions object"); |
128 "attempt to add a Permission to a readonly Permissions object"); |
128 |
129 |
129 PermissionCollection pc; |
130 PermissionCollection pc = getPermissionCollection(permission, true); |
130 |
131 pc.add(permission); |
131 synchronized (this) { |
|
132 pc = getPermissionCollection(permission, true); |
|
133 pc.add(permission); |
|
134 } |
|
135 |
132 |
136 // No sync; staleness -> optimizations delayed, which is OK |
133 // No sync; staleness -> optimizations delayed, which is OK |
137 if (permission instanceof AllPermission) { |
134 if (permission instanceof AllPermission) { |
138 allPermission = pc; |
135 allPermission = pc; |
139 } |
136 } |
167 * |
164 * |
168 * @return true if "permission" is implied by the permissions in the |
165 * @return true if "permission" is implied by the permissions in the |
169 * PermissionCollection it |
166 * PermissionCollection it |
170 * belongs to, false if not. |
167 * belongs to, false if not. |
171 */ |
168 */ |
172 |
169 @Override |
173 public boolean implies(Permission permission) { |
170 public boolean implies(Permission permission) { |
174 // No sync; staleness -> skip optimization, which is OK |
171 // No sync; staleness -> skip optimization, which is OK |
175 if (allPermission != null) { |
172 if (allPermission != null) { |
176 return true; // AllPermission has already been added |
173 return true; // AllPermission has already been added |
177 } else { |
174 } else { |
178 synchronized (this) { |
175 PermissionCollection pc = getPermissionCollection(permission, |
179 PermissionCollection pc = getPermissionCollection(permission, |
176 false); |
180 false); |
177 if (pc != null) { |
181 if (pc != null) { |
178 return pc.implies(permission); |
182 return pc.implies(permission); |
179 } else { |
183 } else { |
180 // none found |
184 // none found |
181 return false; |
185 return false; |
|
186 } |
|
187 } |
182 } |
188 } |
183 } |
189 } |
184 } |
190 |
185 |
191 /** |
186 /** |
192 * Returns an enumeration of all the Permission objects in all the |
187 * Returns an enumeration of all the Permission objects in all the |
193 * PermissionCollections in this Permissions object. |
188 * PermissionCollections in this Permissions object. |
194 * |
189 * |
195 * @return an enumeration of all the Permissions. |
190 * @return an enumeration of all the Permissions. |
196 */ |
191 */ |
197 |
192 @Override |
198 public Enumeration<Permission> elements() { |
193 public Enumeration<Permission> elements() { |
199 // go through each Permissions in the hash table |
194 // go through each Permissions in the hash table |
200 // and call their elements() function. |
195 // and call their elements() function. |
201 |
196 |
202 synchronized (this) { |
197 return new PermissionsEnumerator(permsMap.values().iterator()); |
203 return new PermissionsEnumerator(permsMap.values().iterator()); |
|
204 } |
|
205 } |
198 } |
206 |
199 |
207 /** |
200 /** |
208 * Gets the PermissionCollection in this Permissions object for |
201 * Gets the PermissionCollection in this Permissions object for |
209 * permissions whose type is the same as that of <i>p</i>. |
202 * permissions whose type is the same as that of <i>p</i>. |
234 * implies() because it incurs the additional overhead of creating and |
227 * implies() because it incurs the additional overhead of creating and |
235 * adding an empty PermissionCollection that will just return false. |
228 * adding an empty PermissionCollection that will just return false. |
236 * It should be set to true when invoked from add(). |
229 * It should be set to true when invoked from add(). |
237 */ |
230 */ |
238 private PermissionCollection getPermissionCollection(Permission p, |
231 private PermissionCollection getPermissionCollection(Permission p, |
239 boolean createEmpty) { |
232 boolean createEmpty) { |
240 Class<?> c = p.getClass(); |
233 Class<?> c = p.getClass(); |
241 |
234 |
242 PermissionCollection pc = permsMap.get(c); |
|
243 |
|
244 if (!hasUnresolved && !createEmpty) { |
235 if (!hasUnresolved && !createEmpty) { |
245 return pc; |
236 return permsMap.get(c); |
246 } else if (pc == null) { |
237 } |
247 |
238 |
248 // Check for unresolved permissions |
239 // Create and add permission collection to map if it is absent. |
249 pc = (hasUnresolved ? getUnresolvedPermissions(p) : null); |
240 // NOTE: cannot use lambda for mappingFunction parameter until |
250 |
241 // JDK-8076596 is fixed. |
251 // if still null, create a new collection |
242 return permsMap.computeIfAbsent(c, |
252 if (pc == null && createEmpty) { |
243 new java.util.function.Function<>() { |
253 |
244 @Override |
254 pc = p.newPermissionCollection(); |
245 public PermissionCollection apply(Class<?> k) { |
255 |
246 // Check for unresolved permissions |
256 // still no PermissionCollection? |
247 PermissionCollection pc = |
257 // We'll give them a PermissionsHash. |
248 (hasUnresolved ? getUnresolvedPermissions(p) : null); |
258 if (pc == null) |
249 |
259 pc = new PermissionsHash(); |
250 // if still null, create a new collection |
|
251 if (pc == null && createEmpty) { |
|
252 |
|
253 pc = p.newPermissionCollection(); |
|
254 |
|
255 // still no PermissionCollection? |
|
256 // We'll give them a PermissionsHash. |
|
257 if (pc == null) { |
|
258 pc = new PermissionsHash(); |
|
259 } |
|
260 } |
|
261 return pc; |
|
262 } |
260 } |
263 } |
261 |
264 ); |
262 if (pc != null) { |
|
263 permsMap.put(c, pc); |
|
264 } |
|
265 } |
|
266 return pc; |
|
267 } |
265 } |
268 |
266 |
269 /** |
267 /** |
270 * Resolves any unresolved permissions of type p. |
268 * Resolves any unresolved permissions of type p. |
271 * |
269 * |
275 * or null if there were no unresolved permissions of type p. |
273 * or null if there were no unresolved permissions of type p. |
276 * |
274 * |
277 */ |
275 */ |
278 private PermissionCollection getUnresolvedPermissions(Permission p) |
276 private PermissionCollection getUnresolvedPermissions(Permission p) |
279 { |
277 { |
280 // Called from within synchronized method so permsMap doesn't need lock |
|
281 |
|
282 UnresolvedPermissionCollection uc = |
278 UnresolvedPermissionCollection uc = |
283 (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); |
279 (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); |
284 |
280 |
285 // we have no unresolved permissions if uc is null |
281 // we have no unresolved permissions if uc is null |
286 if (uc == null) |
282 if (uc == null) |
360 // Don't call out.defaultWriteObject() |
356 // Don't call out.defaultWriteObject() |
361 |
357 |
362 // Copy perms into a Hashtable |
358 // Copy perms into a Hashtable |
363 Hashtable<Class<?>, PermissionCollection> perms = |
359 Hashtable<Class<?>, PermissionCollection> perms = |
364 new Hashtable<>(permsMap.size()*2); // no sync; estimate |
360 new Hashtable<>(permsMap.size()*2); // no sync; estimate |
365 synchronized (this) { |
361 perms.putAll(permsMap); |
366 perms.putAll(permsMap); |
|
367 } |
|
368 |
362 |
369 // Write out serializable fields |
363 // Write out serializable fields |
370 ObjectOutputStream.PutField pfields = out.putFields(); |
364 ObjectOutputStream.PutField pfields = out.putFields(); |
371 |
365 |
372 pfields.put("allPermission", allPermission); // no sync; staleness OK |
366 pfields.put("allPermission", allPermission); // no sync; staleness OK |
392 // writeObject writes a Hashtable<Class<?>, PermissionCollection> for |
386 // writeObject writes a Hashtable<Class<?>, PermissionCollection> for |
393 // the perms key, so this cast is safe, unless the data is corrupt. |
387 // the perms key, so this cast is safe, unless the data is corrupt. |
394 @SuppressWarnings("unchecked") |
388 @SuppressWarnings("unchecked") |
395 Hashtable<Class<?>, PermissionCollection> perms = |
389 Hashtable<Class<?>, PermissionCollection> perms = |
396 (Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null); |
390 (Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null); |
397 permsMap = new HashMap<>(perms.size()*2); |
391 permsMap = new ConcurrentHashMap<>(perms.size()*2); |
398 permsMap.putAll(perms); |
392 permsMap.putAll(perms); |
399 |
393 |
400 // Set hasUnresolved |
394 // Set hasUnresolved |
401 UnresolvedPermissionCollection uc = |
395 UnresolvedPermissionCollection uc = |
402 (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); |
396 (UnresolvedPermissionCollection) permsMap.get(UnresolvedPermission.class); |
479 { |
473 { |
480 /** |
474 /** |
481 * Key and value are (same) permissions objects. |
475 * Key and value are (same) permissions objects. |
482 * Not serialized; see serialization section at end of class. |
476 * Not serialized; see serialization section at end of class. |
483 */ |
477 */ |
484 private transient Map<Permission, Permission> permsMap; |
478 private transient ConcurrentHashMap<Permission, Permission> permsMap; |
485 |
479 |
486 /** |
480 /** |
487 * Create an empty PermissionsHash object. |
481 * Create an empty PermissionsHash object. |
488 */ |
482 */ |
489 |
|
490 PermissionsHash() { |
483 PermissionsHash() { |
491 permsMap = new HashMap<>(11); |
484 permsMap = new ConcurrentHashMap<>(11); |
492 } |
485 } |
493 |
486 |
494 /** |
487 /** |
495 * Adds a permission to the PermissionsHash. |
488 * Adds a permission to the PermissionsHash. |
496 * |
489 * |
497 * @param permission the Permission object to add. |
490 * @param permission the Permission object to add. |
498 */ |
491 */ |
499 |
492 @Override |
500 public void add(Permission permission) { |
493 public void add(Permission permission) { |
501 synchronized (this) { |
494 permsMap.put(permission, permission); |
502 permsMap.put(permission, permission); |
|
503 } |
|
504 } |
495 } |
505 |
496 |
506 /** |
497 /** |
507 * Check and see if this set of permissions implies the permissions |
498 * Check and see if this set of permissions implies the permissions |
508 * expressed in "permission". |
499 * expressed in "permission". |
510 * @param permission the Permission object to compare |
501 * @param permission the Permission object to compare |
511 * |
502 * |
512 * @return true if "permission" is a proper subset of a permission in |
503 * @return true if "permission" is a proper subset of a permission in |
513 * the set, false if not. |
504 * the set, false if not. |
514 */ |
505 */ |
515 |
506 @Override |
516 public boolean implies(Permission permission) { |
507 public boolean implies(Permission permission) { |
517 // attempt a fast lookup and implies. If that fails |
508 // attempt a fast lookup and implies. If that fails |
518 // then enumerate through all the permissions. |
509 // then enumerate through all the permissions. |
519 synchronized (this) { |
510 Permission p = permsMap.get(permission); |
520 Permission p = permsMap.get(permission); |
511 |
521 |
512 // If permission is found, then p.equals(permission) |
522 // If permission is found, then p.equals(permission) |
513 if (p == null) { |
523 if (p == null) { |
514 for (Permission p_ : permsMap.values()) { |
524 for (Permission p_ : permsMap.values()) { |
515 if (p_.implies(permission)) |
525 if (p_.implies(permission)) |
516 return true; |
526 return true; |
|
527 } |
|
528 return false; |
|
529 } else { |
|
530 return true; |
|
531 } |
517 } |
|
518 return false; |
|
519 } else { |
|
520 return true; |
532 } |
521 } |
533 } |
522 } |
534 |
523 |
535 /** |
524 /** |
536 * Returns an enumeration of all the Permission objects in the container. |
525 * Returns an enumeration of all the Permission objects in the container. |
537 * |
526 * |
538 * @return an enumeration of all the Permissions. |
527 * @return an enumeration of all the Permissions. |
539 */ |
528 */ |
540 |
529 @Override |
541 public Enumeration<Permission> elements() { |
530 public Enumeration<Permission> elements() { |
542 // Convert Iterator of Map values into an Enumeration |
531 return permsMap.elements(); |
543 synchronized (this) { |
|
544 return Collections.enumeration(permsMap.values()); |
|
545 } |
|
546 } |
532 } |
547 |
533 |
548 private static final long serialVersionUID = -8491988220802933440L; |
534 private static final long serialVersionUID = -8491988220802933440L; |
549 // Need to maintain serialization interoperability with earlier releases, |
535 // Need to maintain serialization interoperability with earlier releases, |
550 // which had the serializable field: |
536 // which had the serializable field: |
568 // Don't call out.defaultWriteObject() |
554 // Don't call out.defaultWriteObject() |
569 |
555 |
570 // Copy perms into a Hashtable |
556 // Copy perms into a Hashtable |
571 Hashtable<Permission, Permission> perms = |
557 Hashtable<Permission, Permission> perms = |
572 new Hashtable<>(permsMap.size()*2); |
558 new Hashtable<>(permsMap.size()*2); |
573 synchronized (this) { |
559 perms.putAll(permsMap); |
574 perms.putAll(permsMap); |
|
575 } |
|
576 |
560 |
577 // Write out serializable fields |
561 // Write out serializable fields |
578 ObjectOutputStream.PutField pfields = out.putFields(); |
562 ObjectOutputStream.PutField pfields = out.putFields(); |
579 pfields.put("perms", perms); |
563 pfields.put("perms", perms); |
580 out.writeFields(); |
564 out.writeFields(); |
595 // writeObject writes a Hashtable<Class<?>, PermissionCollection> for |
579 // writeObject writes a Hashtable<Class<?>, PermissionCollection> for |
596 // the perms key, so this cast is safe, unless the data is corrupt. |
580 // the perms key, so this cast is safe, unless the data is corrupt. |
597 @SuppressWarnings("unchecked") |
581 @SuppressWarnings("unchecked") |
598 Hashtable<Permission, Permission> perms = |
582 Hashtable<Permission, Permission> perms = |
599 (Hashtable<Permission, Permission>)gfields.get("perms", null); |
583 (Hashtable<Permission, Permission>)gfields.get("perms", null); |
600 permsMap = new HashMap<>(perms.size()*2); |
584 permsMap = new ConcurrentHashMap<>(perms.size()*2); |
601 permsMap.putAll(perms); |
585 permsMap.putAll(perms); |
602 } |
586 } |
603 } |
587 } |