blob: f234699d9ec1289f59f51399a704845087510d1b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.framework.internal.core;
import java.security.*;
import java.util.*;
import org.osgi.service.condpermadmin.Condition;
import org.osgi.service.permissionadmin.PermissionInfo;
/**
* This class represents a PermissionCollection tied to a set of Conditions.
* Before the permissions are actually used, isNonEmpty should be called.
*/
public class ConditionalPermissionSet extends BundlePermissionCollection {
private static final long serialVersionUID = 3258411750729920566L;
private ConditionalPermissionInfoImpl cpis[] = ConditionalPermissionAdminImpl.EMPTY_COND_PERM_INFO;
private HashMap cachedPermissionCollections = new HashMap();
private boolean hasAllPermission = false;
private AbstractBundle bundle;
/**
* These are conditions that need to be satisfied in order to enable the
* permissions. If the array is empty, no conditions need to be satisfied.
* If <code>neededCondititions</code> is null,
*/
private Condition neededConditions[];
/*
* TODO: we need to validate the cpis[] to make sure they don't go away.
* Reset everything if they do. We also need to be able to add CPIs
*/
/**
* Construct a new ConditionalPermission set with an initial set of
* permissions.
*/
public ConditionalPermissionSet(AbstractBundle bundle, ConditionalPermissionInfoImpl cpis[], Condition neededConditions[]) {
this.bundle = bundle;
this.cpis = cpis;
this.neededConditions = neededConditions;
checkForAllPermission();
}
/**
* Adds another ConditionalPermissionInfoImpl to this set. <b>Only a
* CondtitionalPermissionInfo whose Conditions are immutable and satisfied
* may be added. </b>
*
* @param cpi the ConditionalPermissionInfoImpl to be added to this set.
* <b>Only a CondtitionalPermissionInfo whose Conditions are
* immutable and satisfied may be added. </b>
*/
void addConditionalPermissionInfo(ConditionalPermissionInfoImpl cpi) {
if (neededConditions == null || neededConditions.length > 0)
throw new RuntimeException("Cannot add ConditionalPermissionInfoImpl to a non satisfied set"); //$NON-NLS-1$
synchronized (cachedPermissionCollections) {
// first look for a null slot
for (int i = 0; i < cpis.length; i++)
if (cpis[i] == null) { // found an empty slot; use it
cpis[i] = cpi;
cachedPermissionCollections.clear();
return;
}
ConditionalPermissionInfoImpl newcpis[] = new ConditionalPermissionInfoImpl[cpis.length + 1];
System.arraycopy(cpis, 0, newcpis, 0, cpis.length);
newcpis[cpis.length] = cpi;
cpis = newcpis;
/*
* TODO: I couldn't decide wether it is better to run through the cached
* PermissionCollections and add permissions from this cpi, or to just
* clear out and let them get rebuilt ondemand. The ondemand route is
* simpler and in the end may be more efficient.
*/
cachedPermissionCollections.clear();
checkForAllPermission();
}
}
/**
* This runs through the PermissionInfos to see if there is an AllPermission in the mix.
*/
private void checkForAllPermission() {
if (hasAllPermission) {
return;
}
out: for (int i = 0; i < cpis.length; i++) {
if (cpis[i] == null) // check for deletions
continue;
PermissionInfo perms[] = cpis[i].perms;
for (int j = 0; j < perms.length; j++) {
if (perms[j].getType().equals(AllPermission.class.getName())) {
hasAllPermission = true;
break out;
}
}
}
}
/**
* Returns true if at least one of the ConditionalPermissionInfos in this
* set is still active. (Not deleted.)
*
* @return true if there is at least one active ConditionalPermissionInfo.
*/
boolean isNonEmpty() {
boolean nonEmpty = false;
boolean forceAllPermCheck = false;
synchronized (cachedPermissionCollections) {
for (int i = 0; i < cpis.length; i++) {
if (cpis[i] != null) {
if (cpis[i].isDeleted()) {
cpis[i] = null;
forceAllPermCheck = true;
/*
* We don't have a way to remove from a collection; we can
* only add. Thus, we must clear out everything. TODO:
* Investigate if it would be more efficient to only clear
* the permission collections of the type stored by the cpi.
*/
cachedPermissionCollections.clear();
} else {
nonEmpty = true;
}
}
}
if (!nonEmpty)
cpis = ConditionalPermissionAdminImpl.EMPTY_COND_PERM_INFO;
if (forceAllPermCheck) {
hasAllPermission = false;
checkForAllPermission();
}
}
return nonEmpty;
}
/**
* Returns the conditions that need to be satisfied before the permissions
* embodied in this set can be used.
*
* @return the array of conditions that need to be satisfied. If the array
* is empty, no conditions need to be satisfied to use the
* permissions. If null is returned, these permissions can never be
* used.
*/
Condition[] getNeededConditions() {
if (neededConditions == null || neededConditions.length == 0)
return neededConditions;
boolean foundNonNullCondition = false;
/* We need to check to see if any conditions became immutable */
for (int i = 0; i < neededConditions.length; i++) {
Condition cond = neededConditions[i];
if (cond == null)
continue;
if (!cond.isMutable()) {
if (cond.isSatisfied()) {
neededConditions[i] = null;
} else {
/*
* We now have an immutable unsatisfied condition, this set
* is no longer valid.
*/
neededConditions = null;
break;
}
} else {
foundNonNullCondition = true;
}
}
if (neededConditions != null && !foundNonNullCondition)
neededConditions = ConditionalPermissionAdminImpl.EMPTY_COND;
return neededConditions;
}
/**
* We don't do anything here since this isn't a real PermissionCollection.
*
* @param perm ignored.
* @see java.security.PermissionCollection#add(java.security.Permission)
*/
public void add(Permission perm) {
// do nothing
}
/**
* Checks to see if the desired Permission is implied by this collection of
* ConditionalPermissionInfos.
*
* @param perm Permission to check.
* @return true if this ConditionPermissionSet implies the passed
* Permission.
* @see java.security.PermissionCollection#implies(java.security.Permission)
*/
public boolean implies(Permission perm) {
if (hasAllPermission)
return true;
Class permClass = perm.getClass();
PermissionCollection collection;
synchronized (cachedPermissionCollections) {
collection = (PermissionCollection) cachedPermissionCollections.get(permClass);
if (collection == null) {
collection = perm.newPermissionCollection();
if (collection == null)
collection = new PermissionsHash();
for (int i = 0; i < cpis.length; i++) {
try {
ConditionalPermissionInfoImpl cpi = cpis[i];
if (cpi != null)
cpi.addPermissions(bundle, collection, permClass);
} catch (Exception e) {
// TODO: we should log this somewhere
e.printStackTrace();
}
}
cachedPermissionCollections.put(permClass, collection);
}
}
return collection.implies(perm);
}
/**
* We don't do anything here since this isn't a real PermissionCollection.
*
* @return always returns null.
* @see java.security.PermissionCollection#elements()
*/
public Enumeration elements() {
return null;
}
/**
* This method simply clears the resolved permission table. I think in both the
* short-term and the amoritized case, this is more efficient than walking through
* and clearing specific entries.
*/
void unresolvePermissions() {
synchronized (cachedPermissionCollections) {
cachedPermissionCollections.clear();
}
}
boolean remove(ConditionalPermissionInfoImpl cpi) {
synchronized (cachedPermissionCollections) {
for (int i = 0; i < cpis.length; i++)
if (cpis[i] == cpi) {
cpis[i] = null;
cachedPermissionCollections.clear();
return true;
}
}
return false;
}
}