/*
 * Copyright (c) OSGi Alliance (2004, 2016). All Rights Reserved.
 * 
 * Licensed 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 org.osgi.framework;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.security.BasicPermission;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;

A bundle's authority to require or provide a bundle or to receive or attach fragments.

A bundle symbolic name defines a unique fully qualified name. Wildcards may be used.

name ::= <symbolic name> | <symbolic name ending in ".*"> | *
Examples:
org.osgi.example.bundle
org.osgi.example.*
 *

BundlePermission has four actions: provide, require, host, and fragment. The provide action implies the require action.

Author:$Id: 7b0816059dc9b3e37f0375039bebbe5f0b18d998 $
Since:1.3
@ThreadSafe
/** * A bundle's authority to require or provide a bundle or to receive or attach * fragments. * * <p> * A bundle symbolic name defines a unique fully qualified name. Wildcards may * be used. * * <pre> * name ::= &lt;symbolic name&gt; | &lt;symbolic name ending in &quot;.*&quot;&gt; | * * </pre> * * Examples: * * <pre> * org.osgi.example.bundle * org.osgi.example.* * * * </pre> * * <p> * {@code BundlePermission} has four actions: {@code provide}, {@code require}, * {@code host}, and {@code fragment}. The {@code provide} action implies the * {@code require} action. * * @since 1.3 * @ThreadSafe * @author $Id: 7b0816059dc9b3e37f0375039bebbe5f0b18d998 $ */
public final class BundlePermission extends BasicPermission { private static final long serialVersionUID = 3257846601685873716L;
The action string provide. The provide action implies the require action.
/** * The action string {@code provide}. The {@code provide} action implies the * {@code require} action. */
public final static String PROVIDE = "provide";
The action string require. The require action is implied by the provide action.
/** * The action string {@code require}. The {@code require} action is implied * by the {@code provide} action. */
public final static String REQUIRE = "require";
The action string host.
/** * The action string {@code host}. */
public final static String HOST = "host";
The action string fragment.
/** * The action string {@code fragment}. */
public final static String FRAGMENT = "fragment"; private final static int ACTION_PROVIDE = 0x00000001; private final static int ACTION_REQUIRE = 0x00000002; private final static int ACTION_HOST = 0x00000004; private final static int ACTION_FRAGMENT = 0x00000008; private final static int ACTION_ALL = ACTION_PROVIDE | ACTION_REQUIRE | ACTION_HOST | ACTION_FRAGMENT; final static int ACTION_NONE = 0;
The actions mask.
/** * The actions mask. */
private transient int action_mask;
The actions in canonical form.
@serial
/** * The actions in canonical form. * * @serial */
private volatile String actions = null;
Defines the authority to provide and/or require and or specify a host fragment symbolic name within the OSGi environment.

Bundle Permissions are granted over all possible versions of a bundle. A bundle that needs to provide a bundle must have the appropriate BundlePermission for the symbolic name; a bundle that requires a bundle must have the appropriate BundlePermssion for that symbolic name; a bundle that specifies a fragment host must have the appropriate BundlePermission for that symbolic name.

Params:
  • symbolicName – The bundle symbolic name.
  • actions – provide,require, host, fragment (canonical order).
/** * Defines the authority to provide and/or require and or specify a host * fragment symbolic name within the OSGi environment. * <p> * Bundle Permissions are granted over all possible versions of a bundle. * * A bundle that needs to provide a bundle must have the appropriate * {@code BundlePermission} for the symbolic name; a bundle that requires a * bundle must have the appropriate {@code BundlePermssion} for that * symbolic name; a bundle that specifies a fragment host must have the * appropriate {@code BundlePermission} for that symbolic name. * * @param symbolicName The bundle symbolic name. * @param actions {@code provide},{@code require}, {@code host}, * {@code fragment} (canonical order). */
public BundlePermission(String symbolicName, String actions) { this(symbolicName, parseActions(actions)); }
Package private constructor used by BundlePermissionCollection.
Params:
  • symbolicName – the bundle symbolic name
  • mask – the action mask
/** * Package private constructor used by BundlePermissionCollection. * * @param symbolicName the bundle symbolic name * @param mask the action mask */
BundlePermission(String symbolicName, int mask) { super(symbolicName); setTransients(mask); }
Called by constructors and when deserialized.
Params:
  • mask –
/** * Called by constructors and when deserialized. * * @param mask */
private synchronized void setTransients(int mask) { if ((mask == ACTION_NONE) || ((mask & ACTION_ALL) != mask)) { throw new IllegalArgumentException("invalid action string"); } action_mask = mask; }
Returns the current action mask.

Used by the BundlePermissionCollection class.

Returns:Current action mask.
/** * Returns the current action mask. * <p> * Used by the BundlePermissionCollection class. * * @return Current action mask. */
synchronized int getActionsMask() { return action_mask; }
Parse action string into action mask.
Params:
  • actions – Action string.
Returns:action mask.
/** * Parse action string into action mask. * * @param actions Action string. * @return action mask. */
private static int parseActions(String actions) { boolean seencomma = false; int mask = ACTION_NONE; if (actions == null) { return mask; } char[] a = actions.toCharArray(); int i = a.length - 1; if (i < 0) return mask; while (i != -1) { char c; // skip whitespace while ((i != -1) && ((c = a[i]) == ' ' || c == '\r' || c == '\n' || c == '\f' || c == '\t')) i--; // check for the known strings int matchlen; if (i >= 6 && (a[i - 6] == 'p' || a[i - 6] == 'P') && (a[i - 5] == 'r' || a[i - 5] == 'R') && (a[i - 4] == 'o' || a[i - 4] == 'O') && (a[i - 3] == 'v' || a[i - 3] == 'V') && (a[i - 2] == 'i' || a[i - 2] == 'I') && (a[i - 1] == 'd' || a[i - 1] == 'D') && (a[i] == 'e' || a[i] == 'E')) { matchlen = 7; mask |= ACTION_PROVIDE | ACTION_REQUIRE; } else if (i >= 6 && (a[i - 6] == 'r' || a[i - 6] == 'R') && (a[i - 5] == 'e' || a[i - 5] == 'E') && (a[i - 4] == 'q' || a[i - 4] == 'Q') && (a[i - 3] == 'u' || a[i - 3] == 'U') && (a[i - 2] == 'i' || a[i - 2] == 'I') && (a[i - 1] == 'r' || a[i - 1] == 'R') && (a[i] == 'e' || a[i] == 'E')) { matchlen = 7; mask |= ACTION_REQUIRE; } else if (i >= 3 && (a[i - 3] == 'h' || a[i - 3] == 'H') && (a[i - 2] == 'o' || a[i - 2] == 'O') && (a[i - 1] == 's' || a[i - 1] == 'S') && (a[i] == 't' || a[i] == 'T')) { matchlen = 4; mask |= ACTION_HOST; } else if (i >= 7 && (a[i - 7] == 'f' || a[i - 7] == 'F') && (a[i - 6] == 'r' || a[i - 6] == 'R') && (a[i - 5] == 'a' || a[i - 5] == 'A') && (a[i - 4] == 'g' || a[i - 4] == 'G') && (a[i - 3] == 'm' || a[i - 3] == 'M') && (a[i - 2] == 'e' || a[i - 2] == 'E') && (a[i - 1] == 'n' || a[i - 1] == 'N') && (a[i] == 't' || a[i] == 'T')) { matchlen = 8; mask |= ACTION_FRAGMENT; } else { // parse error throw new IllegalArgumentException("invalid permission: " + actions); } // make sure we didn't just match the tail of a word // like "ackbarfrequire". Also, skip to the comma. seencomma = false; while (i >= matchlen && !seencomma) { switch (a[i - matchlen]) { case ',' : seencomma = true; /* FALLTHROUGH */ case ' ' : case '\r' : case '\n' : case '\f' : case '\t' : break; default : throw new IllegalArgumentException("invalid permission: " + actions); } i--; } // point i at the location of the comma minus one (or -1). i -= matchlen; } if (seencomma) { throw new IllegalArgumentException("invalid permission: " + actions); } return mask; }
Determines if the specified permission is implied by this object.

This method checks that the symbolic name of the target is implied by the symbolic name of this object. The list of BundlePermission actions must either match or allow for the list of the target object to imply the target BundlePermission action.

The permission to provide a bundle implies the permission to require the named symbolic name.

      x.y.*,"provide" -> x.y.z,"provide" is true
      *,"require" -> x.y, "require"      is true
      *,"provide" -> x.y, "require"      is true
      x.y,"provide" -> x.y.z, "provide"  is false
Params:
  • p – The requested permission.
Returns:true if the specified BundlePermission action is implied by this object; false otherwise.
/** * Determines if the specified permission is implied by this object. * * <p> * This method checks that the symbolic name of the target is implied by the * symbolic name of this object. The list of {@code BundlePermission} * actions must either match or allow for the list of the target object to * imply the target {@code BundlePermission} action. * <p> * The permission to provide a bundle implies the permission to require the * named symbolic name. * * <pre> * x.y.*,&quot;provide&quot; -&gt; x.y.z,&quot;provide&quot; is true * *,&quot;require&quot; -&gt; x.y, &quot;require&quot; is true * *,&quot;provide&quot; -&gt; x.y, &quot;require&quot; is true * x.y,&quot;provide&quot; -&gt; x.y.z, &quot;provide&quot; is false * </pre> * * @param p The requested permission. * @return {@code true} if the specified {@code BundlePermission} action is * implied by this object; {@code false} otherwise. */
@Override public boolean implies(Permission p) { if (!(p instanceof BundlePermission)) { return false; } BundlePermission requested = (BundlePermission) p; final int effective = getActionsMask(); final int desired = requested.getActionsMask(); return ((effective & desired) == desired) && super.implies(requested); }
Returns the canonical string representation of the BundlePermission actions.

Always returns present BundlePermission actions in the following order: provide, require, host, fragment.

Returns:Canonical string representation of the BundlePermission actions.
/** * Returns the canonical string representation of the * {@code BundlePermission} actions. * * <p> * Always returns present {@code BundlePermission} actions in the following * order: {@code provide}, {@code require}, {@code host}, {@code fragment}. * * @return Canonical string representation of the {@code BundlePermission * } actions. */
@Override public String getActions() { String result = actions; if (result == null) { StringBuilder sb = new StringBuilder(); boolean comma = false; if ((action_mask & ACTION_PROVIDE) == ACTION_PROVIDE) { sb.append(PROVIDE); comma = true; } if ((action_mask & ACTION_REQUIRE) == ACTION_REQUIRE) { if (comma) sb.append(','); sb.append(REQUIRE); comma = true; } if ((action_mask & ACTION_HOST) == ACTION_HOST) { if (comma) sb.append(','); sb.append(HOST); comma = true; } if ((action_mask & ACTION_FRAGMENT) == ACTION_FRAGMENT) { if (comma) sb.append(','); sb.append(FRAGMENT); } actions = result = sb.toString(); } return result; }
Returns a new PermissionCollection object suitable for storing BundlePermission objects.
Returns:A new PermissionCollection object.
/** * Returns a new {@code PermissionCollection} object suitable for storing * {@code BundlePermission} objects. * * @return A new {@code PermissionCollection} object. */
@Override public PermissionCollection newPermissionCollection() { return new BundlePermissionCollection(); }
Determines the equality of two BundlePermission objects. This method checks that specified bundle has the same bundle symbolic name and BundlePermission actions as this BundlePermission object.
Params:
  • obj – The object to test for equality with this BundlePermission object.
Returns:true if obj is a BundlePermission, and has the same bundle symbolic name and actions as this BundlePermission object; false otherwise.
/** * Determines the equality of two {@code BundlePermission} objects. * * This method checks that specified bundle has the same bundle symbolic * name and {@code BundlePermission} actions as this * {@code BundlePermission} object. * * @param obj The object to test for equality with this * {@code BundlePermission} object. * @return {@code true} if {@code obj} is a {@code BundlePermission}, and * has the same bundle symbolic name and actions as this * {@code BundlePermission} object; {@code false} otherwise. */
@Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BundlePermission)) { return false; } BundlePermission bp = (BundlePermission) obj; return (getActionsMask() == bp.getActionsMask()) && getName().equals(bp.getName()); }
Returns the hash code value for this object.
Returns:A hash code value for this object.
/** * Returns the hash code value for this object. * * @return A hash code value for this object. */
@Override public int hashCode() { int h = 31 * 17 + getName().hashCode(); h = 31 * h + getActions().hashCode(); return h; }
WriteObject is called to save the state of the BundlePermission object to a stream. The actions are serialized, and the superclass takes care of the name.
/** * WriteObject is called to save the state of the {@code BundlePermission} * object to a stream. The actions are serialized, and the superclass takes * care of the name. */
private synchronized void writeObject(java.io.ObjectOutputStream s) throws IOException { // Write out the actions. The superclass takes care of the name // call getActions to make sure actions field is initialized if (actions == null) getActions(); s.defaultWriteObject(); }
readObject is called to restore the state of the BundlePermission from a stream.
/** * readObject is called to restore the state of the BundlePermission from a * stream. */
private synchronized void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the action, then initialize the rest s.defaultReadObject(); setTransients(parseActions(actions)); } }
Stores a set of BundlePermission permissions.
See Also:
/** * Stores a set of {@code BundlePermission} permissions. * * @see java.security.Permission * @see java.security.Permissions * @see java.security.PermissionCollection */
final class BundlePermissionCollection extends PermissionCollection { private static final long serialVersionUID = 3258407326846433079L;
Table of permissions.
@GuardedBythis
/** * Table of permissions. * * @GuardedBy this */
private transient Map<String, BundlePermission> permissions;
Boolean saying if "*" is in the collection.
@serial
@GuardedBythis
/** * Boolean saying if "*" is in the collection. * * @serial * @GuardedBy this */
private boolean all_allowed;
Create an empty BundlePermissions object.
/** * Create an empty BundlePermissions object. * */
public BundlePermissionCollection() { permissions = new HashMap<String, BundlePermission>(); all_allowed = false; }
Add a permission to this permission collection.
Params:
  • permission – The BundlePermission object to add.
Throws:
/** * Add a permission to this permission collection. * * @param permission The {@code BundlePermission} object to add. * @throws IllegalArgumentException If the permission is not a * {@code BundlePermission} instance. * @throws SecurityException If this {@code BundlePermissionCollection} * object has been marked read-only. */
@Override public void add(final Permission permission) { if (!(permission instanceof BundlePermission)) { throw new IllegalArgumentException("invalid permission: " + permission); } if (isReadOnly()) { throw new SecurityException("attempt to add a Permission to a " + "readonly PermissionCollection"); } final BundlePermission bp = (BundlePermission) permission; final String name = bp.getName(); synchronized (this) { Map<String, BundlePermission> pc = permissions; BundlePermission existing = pc.get(name); if (existing != null) { final int oldMask = existing.getActionsMask(); final int newMask = bp.getActionsMask(); if (oldMask != newMask) { pc.put(name, new BundlePermission(name, oldMask | newMask)); } } else { pc.put(name, bp); } if (!all_allowed) { if (name.equals("*")) all_allowed = true; } } }
Determines if the specified permissions implies the permissions expressed in permission.
Params:
  • permission – The Permission object to compare with this BundlePermission object.
Returns:true if permission is a proper subset of a permission in the set; false otherwise.
/** * Determines if the specified permissions implies the permissions expressed * in {@code permission}. * * @param permission The Permission object to compare with this * {@code BundlePermission} object. * @return {@code true} if {@code permission} is a proper subset of a * permission in the set; {@code false} otherwise. */
@Override public boolean implies(final Permission permission) { if (!(permission instanceof BundlePermission)) { return false; } BundlePermission requested = (BundlePermission) permission; String requestedName = requested.getName(); final int desired = requested.getActionsMask(); int effective = BundlePermission.ACTION_NONE; BundlePermission bp; synchronized (this) { Map<String, BundlePermission> pc = permissions; /* short circuit if the "*" Permission was added */ if (all_allowed) { bp = pc.get("*"); if (bp != null) { effective |= bp.getActionsMask(); if ((effective & desired) == desired) { return true; } } } bp = pc.get(requestedName); // strategy: // Check for full match first. Then work our way up the // name looking for matches on a.b.* if (bp != null) { // we have a direct hit! effective |= bp.getActionsMask(); if ((effective & desired) == desired) { return true; } } // work our way up the tree... int last; int offset = requestedName.length() - 1; while ((last = requestedName.lastIndexOf(".", offset)) != -1) { requestedName = requestedName.substring(0, last + 1) + "*"; bp = pc.get(requestedName); if (bp != null) { effective |= bp.getActionsMask(); if ((effective & desired) == desired) { return true; } } offset = last - 1; } // we don't have to check for "*" as it was already checked // at the top (all_allowed), so we just return false return false; } }
Returns an enumeration of all BundlePermission objects in the container.
Returns:Enumeration of all BundlePermission objects.
/** * Returns an enumeration of all {@code BundlePermission} objects in the * container. * * @return Enumeration of all {@code BundlePermission} objects. */
@Override public synchronized Enumeration<Permission> elements() { List<Permission> all = new ArrayList<Permission>(permissions.values()); return Collections.enumeration(all); } /* serialization logic */ private static final ObjectStreamField[] serialPersistentFields = {new ObjectStreamField("permissions", Hashtable.class), new ObjectStreamField("all_allowed", Boolean.TYPE)}; private synchronized void writeObject(ObjectOutputStream out) throws IOException { Hashtable<String, BundlePermission> hashtable = new Hashtable<String, BundlePermission>(permissions); ObjectOutputStream.PutField pfields = out.putFields(); pfields.put("permissions", hashtable); pfields.put("all_allowed", all_allowed); out.writeFields(); } private synchronized void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { ObjectInputStream.GetField gfields = in.readFields(); @SuppressWarnings("unchecked") Hashtable<String, BundlePermission> hashtable = (Hashtable<String, BundlePermission>) gfields.get("permissions", null); permissions = new HashMap<String, BundlePermission>(hashtable); all_allowed = gfields.get("all_allowed", false); } }