FELIX-514 Updated compendium bundle to R4.1 OSGi API
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@681945 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorPermission.java b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorPermission.java
new file mode 100644
index 0000000..b0ca12d
--- /dev/null
+++ b/org.osgi.compendium/src/main/java/org/osgi/service/monitor/MonitorPermission.java
@@ -0,0 +1,366 @@
+/*
+ * $Header: /cvshome/build/org.osgi.service.monitor/src/org/osgi/service/monitor/MonitorPermission.java,v 1.17 2006/06/21 15:17:16 hargrave Exp $
+ *
+ * Copyright (c) OSGi Alliance (2005, 2006). 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.service.monitor;
+
+import java.io.UnsupportedEncodingException;
+import java.security.Permission;
+import java.util.StringTokenizer;
+
+/**
+ * Indicates the callers authority to publish, read or reset
+ * <code>StatusVariable</code>s, to switch event sending on or off or to
+ * start monitoring jobs. The target of the permission is the identifier of the
+ * <code>StatusVariable</code>, the action can be <code>read</code>,
+ * <code>publish</code>, <code>reset</code>, <code>startjob</code>,
+ * <code>switchevents</code>, or the combination of these separated by
+ * commas. Action names are interpreted case-insensitively, but the canonical
+ * action string returned by {@link #getActions} uses the forms defined by the
+ * action constants.
+ * <p>
+ * If the wildcard <code>*</code> appears in the actions field, all legal
+ * monitoring commands are allowed on the designated target(s) by the owner of
+ * the permission.
+ */
+public class MonitorPermission extends Permission {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -9084425194463274314L;
+
+ /**
+ * Holders of <code>MonitorPermission</code> with the <code>read</code>
+ * action present are allowed to read the value of the
+ * <code>StatusVariable</code>s specified in the permission's target field.
+ */
+ public static final String READ = "read";
+
+ /**
+ * Holders of <code>MonitorPermission</code> with the <code>reset</code>
+ * action present are allowed to reset the value of the
+ * <code>StatusVariable</code>s specified in the permission's target field.
+ */
+ public static final String RESET = "reset";
+
+ /**
+ * Holders of <code>MonitorPermission</code> with the <code>publish</code>
+ * action present are <code>Monitorable</code> services that are allowed
+ * to publish the <code>StatusVariable</code>s specified in the
+ * permission's target field. Note, that this permission cannot be enforced
+ * when a <code>Monitorable</code> registers to the framework, because the
+ * Service Registry does not know about this permission. Instead, any
+ * <code>StatusVariable</code>s published by a <code>Monitorable</code>
+ * without the corresponding <code>publish</code> permission are silently
+ * ignored by <code>MonitorAdmin</code>, and are therefore invisible to the
+ * users of the monitoring service.
+ */
+ public static final String PUBLISH = "publish";
+
+ /**
+ * Holders of <code>MonitorPermission</code> with the <code>startjob</code>
+ * action present are allowed to initiate monitoring jobs involving the
+ * <code>StatusVariable</code>s specified in the permission's target field.
+ * <p>
+ * A minimal sampling interval can be optionally defined in the following
+ * form: <code>startjob:n</code>. This allows the holder of the permission
+ * to initiate time based jobs with a measurement interval of at least
+ * <code>n</code> seconds. If <code>n</code> is not specified or 0 then the
+ * holder of this permission is allowed to start monitoring jobs specifying
+ * any frequency.
+ */
+ public static final String STARTJOB = "startjob";
+
+ /**
+ * Holders of <code>MonitorPermission</code> with the
+ * <code>switchevents</code> action present are allowed to switch event
+ * sending on or off for the value of the <code>StatusVariable</code>s
+ * specified in the permission's target field.
+ */
+ public static final String SWITCHEVENTS = "switchevents";
+
+ private static final int READ_FLAG = 0x1;
+ private static final int RESET_FLAG = 0x2;
+ private static final int PUBLISH_FLAG = 0x4;
+ private static final int STARTJOB_FLAG = 0x8;
+ private static final int SWITCHEVENTS_FLAG = 0x10;
+
+ private static final int ALL_FLAGS = READ_FLAG | RESET_FLAG |
+ PUBLISH_FLAG | STARTJOB_FLAG | SWITCHEVENTS_FLAG;
+
+ private String monId;
+ private String varId;
+ private boolean prefixMonId;
+ private boolean prefixVarId;
+ private int mask;
+ private int minJobInterval;
+
+ /**
+ * Create a <code>MonitorPermission</code> object, specifying the target
+ * and actions.
+ * <p>
+ * The <code>statusVariable</code> parameter is the target of the
+ * permission, defining one or more status variable names to which the
+ * specified actions apply. Multiple status variable names can be selected
+ * by using the wildcard <code>*</code> in the target string. The wildcard
+ * is allowed in both fragments, but only at the end of the fragments.
+ * <p>
+ * For example, the following targets are valid:
+ * <code>com.mycomp.myapp/queue_length</code>,
+ * <code>com.mycomp.myapp/*</code>, <code>com.mycomp.*/*</code>,
+ * <code>*/*</code>, <code>*/queue_length</code>,
+ * <code>*/queue*</code>.
+ * <p>
+ * The following targets are invalid:
+ * <code>*.myapp/queue_length</code>, <code>com.*.myapp/*</code>,
+ * <code>*</code>.
+ * <p>
+ * The <code>actions</code> parameter specifies the allowed action(s):
+ * <code>read</code>, <code>publish</code>, <code>startjob</code>,
+ * <code>reset</code>, <code>switchevents</code>, or the combination of
+ * these separated by commas. String constants are defined in this class for
+ * each valid action. Passing <code>"*"</code> as the action
+ * string is equivalent to listing all actions.
+ *
+ * @param statusVariable the identifier of the <code>StatusVariable</code>
+ * in [Monitorable_id]/[StatusVariable_id] format
+ * @param actions the list of allowed actions separated by commas, or
+ * <code>*</code> for all actions
+ * @throws java.lang.IllegalArgumentException if either parameter is
+ * <code>null</code>, or invalid with regard to the constraints
+ * defined above and in the documentation of the used actions
+ */
+ public MonitorPermission(String statusVariable, String actions)
+ throws IllegalArgumentException {
+ super(statusVariable);
+
+ if(statusVariable == null)
+ throw new IllegalArgumentException(
+ "Invalid StatusVariable path 'null'.");
+
+ if(actions == null)
+ throw new IllegalArgumentException(
+ "Invalid actions string 'null'.");
+
+ int sep = statusVariable.indexOf('/');
+ int len = statusVariable.length();
+
+ if (sep == -1)
+ throw new IllegalArgumentException(
+ "Invalid StatusVariable path: should contain '/' separator.");
+ if (sep == 0 || sep == statusVariable.length() - 1)
+ throw new IllegalArgumentException(
+ "Invalid StatusVariable path: empty monitorable ID or StatusVariable name.");
+
+ prefixMonId = statusVariable.charAt(sep - 1) == '*';
+ prefixVarId = statusVariable.charAt(len - 1) == '*';
+
+ monId = statusVariable.substring(0, prefixMonId ? sep - 1 : sep);
+ varId = statusVariable.substring(sep + 1, prefixVarId ? len - 1 : len);
+
+ checkId(monId, "Monitorable ID part of the target");
+ checkId(varId, "Status Variable ID part of the target");
+
+ minJobInterval = 0;
+
+ if(actions.equals("*"))
+ mask = ALL_FLAGS;
+ else {
+ mask = 0;
+ StringTokenizer st = new StringTokenizer(actions, ",");
+ while (st.hasMoreTokens()) {
+ String action = st.nextToken();
+ if (action.equalsIgnoreCase(READ)) {
+ addToMask(READ_FLAG, READ);
+ } else if (action.equalsIgnoreCase(RESET)) {
+ addToMask(RESET_FLAG, RESET);
+ } else if (action.equalsIgnoreCase(PUBLISH)) {
+ addToMask(PUBLISH_FLAG, PUBLISH);
+ } else if (action.equalsIgnoreCase(SWITCHEVENTS)) {
+ addToMask(SWITCHEVENTS_FLAG, SWITCHEVENTS);
+ } else if (action.toLowerCase().startsWith(STARTJOB)) {
+ minJobInterval = 0;
+
+ int slen = STARTJOB.length();
+ if (action.length() != slen) {
+ if (action.charAt(slen) != ':')
+ throw new IllegalArgumentException(
+ "Invalid action '" + action + "'.");
+
+ try {
+ minJobInterval = Integer.parseInt(action
+ .substring(slen + 1));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(
+ "Invalid parameter in startjob action '"
+ + action + "'.");
+ }
+ }
+ addToMask(STARTJOB_FLAG, STARTJOB);
+ } else
+ throw new IllegalArgumentException("Invalid action '" +
+ action + "'");
+ }
+ }
+ }
+
+ private void addToMask(int action, String actionString) {
+ if((mask & action) != 0)
+ throw new IllegalArgumentException("Invalid action string: " +
+ actionString + " appears multiple times.");
+
+ mask |= action;
+ }
+
+ private void checkId(String id, String idName)
+ throws IllegalArgumentException {
+
+ byte[] nameBytes;
+ try {
+ nameBytes = id.getBytes("UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // never happens, "UTF-8" must always be supported
+ throw new IllegalStateException(e.getMessage());
+ }
+ if(nameBytes.length > StatusVariable.MAX_ID_LENGTH)
+ throw new IllegalArgumentException(idName + " is too long (over " +
+ StatusVariable.MAX_ID_LENGTH + " bytes in UTF-8 encoding).");
+
+
+ if (id.equals(".") || id.equals(".."))
+ throw new IllegalArgumentException(idName + " is invalid.");
+
+ char[] chars = id.toCharArray();
+ for (int i = 0; i < chars.length; i++)
+ if (StatusVariable.SYMBOLIC_NAME_CHARACTERS.indexOf(chars[i]) == -1)
+ throw new IllegalArgumentException(idName +
+ " contains invalid characters.");
+ }
+
+ /**
+ * Create an integer hash of the object. The hash codes of
+ * <code>MonitorPermission</code>s <code>p1</code> and <code>p2</code> are
+ * the same if <code>p1.equals(p2)</code>.
+ *
+ * @return the hash of the object
+ */
+ public int hashCode() {
+ return new Integer(mask).hashCode()
+ ^ new Integer(minJobInterval).hashCode() ^ monId.hashCode()
+ ^ new Boolean(prefixMonId).hashCode()
+ ^ varId.hashCode()
+ ^ new Boolean(prefixVarId).hashCode();
+ }
+
+ /**
+ * Determines the equality of two <code>MonitorPermission</code> objects.
+ * Two <code>MonitorPermission</code> objects are equal if their target
+ * strings are equal and the same set of actions are listed in their action
+ * strings.
+ *
+ * @param o the object being compared for equality with this object
+ * @return <code>true</code> if the two permissions are equal
+ */
+ public boolean equals(Object o) {
+ if (!(o instanceof MonitorPermission))
+ return false;
+
+ MonitorPermission other = (MonitorPermission) o;
+
+ return mask == other.mask && minJobInterval == other.minJobInterval
+ && monId.equals(other.monId)
+ && prefixMonId == other.prefixMonId
+ && varId.equals(other.varId)
+ && prefixVarId == other.prefixVarId;
+ }
+
+ /**
+ * Get the action string associated with this permission. The actions are
+ * returned in the following order: <code>read</code>, <code>reset</code>,
+ * <code>publish</code>, <code>startjob</code>, <code>switchevents</code>.
+ *
+ * @return the allowed actions separated by commas, cannot be
+ * <code>null</code>
+ */
+ public String getActions() {
+ StringBuffer sb = new StringBuffer();
+
+ appendAction(sb, READ_FLAG, READ);
+ appendAction(sb, RESET_FLAG, RESET);
+ appendAction(sb, PUBLISH_FLAG, PUBLISH);
+ appendAction(sb, STARTJOB_FLAG, STARTJOB);
+ appendAction(sb, SWITCHEVENTS_FLAG, SWITCHEVENTS);
+
+ return sb.toString();
+ }
+
+ private void appendAction(StringBuffer sb, int flag, String actionName) {
+ if ((mask & flag) != 0) {
+ if(sb.length() != 0)
+ sb.append(',');
+ sb.append(actionName);
+
+ if(flag == STARTJOB_FLAG && minJobInterval != 0)
+ sb.append(':').append(minJobInterval);
+ }
+ }
+
+ /**
+ * Determines if the specified permission is implied by this permission.
+ * <p>
+ * This method returns <code>false</code> if and only if at least one of the
+ * following conditions are fulfilled for the specified permission:
+ * <ul>
+ * <li>it is not a <code>MonitorPermission</code>
+ * <li>it has a broader set of actions allowed than this one
+ * <li>it allows initiating time based monitoring jobs with a lower minimal
+ * sampling interval
+ * <li>the target set of <code>Monitorable</code>s is not the same nor a
+ * subset of the target set of <code>Monitorable</code>s of this permission
+ * <li>the target set of <code>StatusVariable</code>s is not the same
+ * nor a subset of the target set of <code>StatusVariable</code>s of this
+ * permission
+ * </ul>
+ *
+ * @param p the permission to be checked
+ * @return <code>true</code> if the given permission is implied by this
+ * permission
+ */
+ public boolean implies(Permission p) {
+ if (!(p instanceof MonitorPermission))
+ return false;
+
+ MonitorPermission other = (MonitorPermission) p;
+
+ if ((mask & other.mask) != other.mask)
+ return false;
+
+ if ((other.mask & STARTJOB_FLAG) != 0
+ && minJobInterval > other.minJobInterval)
+ return false;
+
+ return implies(monId, prefixMonId, other.monId, other.prefixMonId)
+ && implies(varId, prefixVarId, other.varId, other.prefixVarId);
+ }
+
+ private boolean implies(String id, boolean prefix, String oid,
+ boolean oprefix) {
+
+ return prefix ? oid.startsWith(id) : !oprefix && id.equals(oid);
+ }
+}