blob: 356a0722f6c8c67f0db1380dc7db1ae6b689a76a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.security;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* {@code Permissions} represents a {@code PermissionCollection} where the
* contained permissions can be of different types. The permissions are
* organized in their appropriate {@code PermissionCollection} obtained by
* {@link Permission#newPermissionCollection()}. For permissions which do not
* provide a dedicated {@code PermissionCollection}, a default permission
* collection, based on a hash table, will be used.
*/
public final class Permissions extends PermissionCollection implements
Serializable {
private static final long serialVersionUID = 4858622370623524688L;
private static final ObjectStreamField[] serialPersistentFields = {
new ObjectStreamField("perms", Hashtable.class),
new ObjectStreamField("allPermission", PermissionCollection.class), };
// Hash to store PermissionCollection's
private transient Map klasses = new HashMap();
private boolean allEnabled; // = false;
/**
* Adds the given {@code Permission} to this heterogeneous {@code
* PermissionCollection}. The {@code permission} is stored in its
* appropriate {@code PermissionCollection}.
*
* @param permission
* the {@code Permission} to be added.
* @throws SecurityException
* if this collection's {@link #isReadOnly()} method returns
* {@code true}.
* @throws NullPointerException
* if {@code permission} is {@code null}.
*/
public void add(Permission permission) {
if (isReadOnly()) {
throw new SecurityException("collection is read-only");
}
if (permission == null) {
throw new NullPointerException("permission == null");
}
Class klass = permission.getClass();
PermissionCollection klassMates = (PermissionCollection)klasses
.get(klass);
if (klassMates == null) {
synchronized (klasses) {
klassMates = (PermissionCollection)klasses.get(klass);
if (klassMates == null) {
klassMates = permission.newPermissionCollection();
if (klassMates == null) {
klassMates = new PermissionsHash();
}
klasses.put(klass, klassMates);
}
}
}
klassMates.add(permission);
if (klass == AllPermission.class) {
allEnabled = true;
}
}
public Enumeration<Permission> elements() {
return new MetaEnumeration(klasses.values().iterator());
}
/**
* An auxiliary implementation for enumerating individual permissions from a
* collection of PermissionCollections.
*
*/
final static class MetaEnumeration implements Enumeration {
private Iterator pcIter;
private Enumeration current;
/**
* Initiates this enumeration.
*
* @param outer an iterator over external collection of
* PermissionCollections
*/
public MetaEnumeration(Iterator outer) {
pcIter = outer;
current = getNextEnumeration();
}
private Enumeration getNextEnumeration() {
while (pcIter.hasNext()) {
Enumeration en = ((PermissionCollection)pcIter.next())
.elements();
if (en.hasMoreElements()) {
return en;
}
}
return null;
}
/**
* Indicates if there are more elements to enumerate.
*/
public boolean hasMoreElements() {
return current != null /* && current.hasMoreElements() */;
}
/**
* Returns next element.
*/
public Object nextElement() {
if (current != null) {
//assert current.hasMoreElements();
Object next = current.nextElement();
if (!current.hasMoreElements()) {
current = getNextEnumeration();
}
return next;
}
throw new NoSuchElementException();
}
}
public boolean implies(Permission permission) {
if (permission == null) {
// RI compatible
throw new NullPointerException("permission == null");
}
if (allEnabled) {
return true;
}
Class klass = permission.getClass();
PermissionCollection klassMates = null;
UnresolvedPermissionCollection billets = (UnresolvedPermissionCollection)klasses
.get(UnresolvedPermission.class);
if (billets != null && billets.hasUnresolved(permission)) {
// try to fill up klassMates with freshly resolved permissions
synchronized (klasses) {
klassMates = (PermissionCollection)klasses.get(klass);
try {
klassMates = billets.resolveCollection(permission,
klassMates);
} catch (Exception ignore) {
//TODO log warning
ignore.printStackTrace();
}
if (klassMates != null) {
//maybe klassMates were just created
// so put them into common map
klasses.put(klass, klassMates);
// very uncommon case, but not improbable one
if (klass == AllPermission.class) {
allEnabled = true;
}
}
}
} else {
klassMates = (PermissionCollection)klasses.get(klass);
}
if (klassMates != null) {
return klassMates.implies(permission);
}
return false;
}
/**
* Reads the object from stream and checks for consistency.
*/
private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
ObjectInputStream.GetField fields = in.readFields();
Map perms = (Map)fields.get("perms", null);
klasses = new HashMap();
synchronized (klasses) {
for (Iterator iter = perms.entrySet().iterator(); iter.hasNext();) {
Map.Entry entry = (Map.Entry) iter.next();
Class key = (Class) entry.getKey();
PermissionCollection pc = (PermissionCollection) entry.getValue();
if (key != pc.elements().nextElement().getClass()) {
throw new InvalidObjectException("collection is corrupted");
}
klasses.put(key, pc);
}
}
allEnabled = fields.get("allPermission", null) != null;
if (allEnabled && !klasses.containsKey(AllPermission.class)) {
throw new InvalidObjectException("all-enabled flag is corrupted");
}
}
/**
* Outputs fields via default mechanism.
*/
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
ObjectOutputStream.PutField fields = out.putFields();
fields.put("perms", new Hashtable(klasses));
fields.put("allPermission", allEnabled ? klasses
.get(AllPermission.class) : null);
out.writeFields();
}
}