/*
* 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 ::= <symbolic name> | <symbolic name ending in ".*"> | *
* </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.*,"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
* </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.
@GuardedBy this
/**
* Table of permissions.
*
* @GuardedBy this
*/
private transient Map<String, BundlePermission> permissions;
Boolean saying if "*" is in the collection.
@serial @GuardedBy this
/**
* 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: - IllegalArgumentException – If the permission is not a
BundlePermission
instance. - SecurityException – If this
BundlePermissionCollection
object has been marked read-only.
/**
* 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);
}
}