| /******************************************************************************* |
| * 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; |
| } |
| } |