MBeanServerPermission.java revision 0
0N/A/*
2362N/A * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Sun designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Sun in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
2362N/A * CA 95054 USA or visit www.sun.com if you need additional information or
2362N/A * have any questions.
0N/A */
0N/A
0N/Apackage javax.management;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.ObjectInputStream;
0N/Aimport java.security.BasicPermission;
0N/Aimport java.security.Permission;
0N/Aimport java.security.PermissionCollection;
0N/Aimport java.util.Collections;
0N/Aimport java.util.Enumeration;
0N/Aimport java.util.Set;
0N/Aimport java.util.StringTokenizer;
0N/A
0N/A/** A Permission to perform actions related to MBeanServers.
0N/A The <em>name</em> of the permission specifies the operation requested
0N/A or granted by the permission. For a granted permission, it can be
0N/A <code>*</code> to allow all of the MBeanServer operations specified below.
0N/A Otherwise, for a granted or requested permission, it must be one of the
0N/A following:
0N/A <dl>
0N/A <dt>createMBeanServer</dt>
0N/A <dd>Create a new MBeanServer object using the method
0N/A {@link MBeanServerFactory#createMBeanServer()} or
0N/A {@link MBeanServerFactory#createMBeanServer(java.lang.String)}.
0N/A <dt>findMBeanServer</dt>
0N/A <dd>Find an MBeanServer with a given name, or all MBeanServers in this
0N/A JVM, using the method {@link MBeanServerFactory#findMBeanServer}.
0N/A <dt>newMBeanServer</dt>
0N/A <dd>Create a new MBeanServer object without keeping a reference to it,
0N/A using the method {@link MBeanServerFactory#newMBeanServer()} or
0N/A {@link MBeanServerFactory#newMBeanServer(java.lang.String)}.
0N/A <dt>releaseMBeanServer</dt>
0N/A <dd>Remove the MBeanServerFactory's reference to an MBeanServer,
0N/A using the method {@link MBeanServerFactory#releaseMBeanServer}.
0N/A </dl>
0N/A The <em>name</em> of the permission can also denote a list of one or more
0N/A comma-separated operations. Spaces are allowed at the beginning and
0N/A end of the <em>name</em> and before and after commas.
0N/A <p>
0N/A <code>MBeanServerPermission("createMBeanServer")</code> implies
0N/A <code>MBeanServerPermission("newMBeanServer")</code>.
0N/A *
0N/A * @since 1.5
0N/A */
0N/Apublic class MBeanServerPermission extends BasicPermission {
0N/A private static final long serialVersionUID = -5661980843569388590L;
0N/A
0N/A private final static int
0N/A CREATE = 0,
0N/A FIND = 1,
0N/A NEW = 2,
0N/A RELEASE = 3,
0N/A N_NAMES = 4;
0N/A
0N/A private final static String[] names = {
0N/A "createMBeanServer",
0N/A "findMBeanServer",
0N/A "newMBeanServer",
0N/A "releaseMBeanServer",
0N/A };
0N/A
0N/A private final static int
0N/A CREATE_MASK = 1<<CREATE,
0N/A FIND_MASK = 1<<FIND,
0N/A NEW_MASK = 1<<NEW,
0N/A RELEASE_MASK = 1<<RELEASE,
0N/A ALL_MASK = CREATE_MASK|FIND_MASK|NEW_MASK|RELEASE_MASK;
0N/A
0N/A /*
0N/A * Map from permission masks to canonical names. This array is
0N/A * filled in on demand.
0N/A *
0N/A * This isn't very scalable. If we have more than five or six
0N/A * permissions, we should consider doing this differently,
0N/A * e.g. with a Map.
0N/A */
0N/A private final static String[] canonicalNames = new String[1 << N_NAMES];
0N/A
0N/A /*
0N/A * The target names mask. This is not private to avoid having to
0N/A * generate accessor methods for accesses from the collection class.
0N/A *
0N/A * This mask includes implied bits. So if it has CREATE_MASK then
0N/A * it necessarily has NEW_MASK too.
0N/A */
0N/A transient int mask;
0N/A
0N/A /** <p>Create a new MBeanServerPermission with the given name.</p>
0N/A <p>This constructor is equivalent to
0N/A <code>MBeanServerPermission(name,null)</code>.</p>
0N/A @param name the name of the granted permission. It must
0N/A respect the constraints spelt out in the description of the
0N/A {@link MBeanServerPermission} class.
0N/A @exception NullPointerException if the name is null.
0N/A @exception IllegalArgumentException if the name is not
0N/A <code>*</code> or one of the allowed names or a comma-separated
0N/A list of the allowed names.
0N/A */
0N/A public MBeanServerPermission(String name) {
0N/A this(name, null);
0N/A }
0N/A
0N/A /** <p>Create a new MBeanServerPermission with the given name.</p>
0N/A @param name the name of the granted permission. It must
0N/A respect the constraints spelt out in the description of the
0N/A {@link MBeanServerPermission} class.
0N/A @param actions the associated actions. This parameter is not
0N/A currently used and must be null or the empty string.
0N/A @exception NullPointerException if the name is null.
0N/A @exception IllegalArgumentException if the name is not
0N/A <code>*</code> or one of the allowed names or a comma-separated
0N/A list of the allowed names, or if <code>actions</code> is a non-null
0N/A non-empty string.
0N/A *
0N/A * @throws NullPointerException if <code>name</code> is <code>null</code>.
0N/A * @throws IllegalArgumentException if <code>name</code> is empty or
0N/A * if arguments are invalid.
0N/A */
0N/A public MBeanServerPermission(String name, String actions) {
0N/A super(getCanonicalName(parseMask(name)), actions);
0N/A
0N/A /* It's annoying to have to parse the name twice, but since
0N/A Permission.getName() is final and since we can't access "this"
0N/A until after the call to the superclass constructor, there
0N/A isn't any very clean way to do this. MBeanServerPermission
0N/A objects aren't constructed very often, luckily. */
0N/A mask = parseMask(name);
0N/A
0N/A /* Check that actions is a null empty string */
0N/A if (actions != null && actions.length() > 0)
0N/A throw new IllegalArgumentException("MBeanServerPermission " +
0N/A "actions must be null: " +
0N/A actions);
0N/A }
MBeanServerPermission(int mask) {
super(getCanonicalName(mask));
this.mask = impliedMask(mask);
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
mask = parseMask(getName());
}
static int simplifyMask(int mask) {
if ((mask & CREATE_MASK) != 0)
mask &= ~NEW_MASK;
return mask;
}
static int impliedMask(int mask) {
if ((mask & CREATE_MASK) != 0)
mask |= NEW_MASK;
return mask;
}
static String getCanonicalName(int mask) {
if (mask == ALL_MASK)
return "*";
mask = simplifyMask(mask);
synchronized (canonicalNames) {
if (canonicalNames[mask] == null)
canonicalNames[mask] = makeCanonicalName(mask);
}
return canonicalNames[mask];
}
private static String makeCanonicalName(int mask) {
final StringBuilder buf = new StringBuilder();
for (int i = 0; i < N_NAMES; i++) {
if ((mask & (1<<i)) != 0) {
if (buf.length() > 0)
buf.append(',');
buf.append(names[i]);
}
}
return buf.toString().intern();
/* intern() avoids duplication when the mask has only
one bit, so is equivalent to the string constants
we have for the names[] array. */
}
/* Convert the string into a bitmask, including bits that
are implied by the permissions in the string. */
private static int parseMask(String name) {
/* Check that target name is a non-null non-empty string */
if (name == null) {
throw new NullPointerException("MBeanServerPermission: " +
"target name can't be null");
}
name = name.trim();
if (name.equals("*"))
return ALL_MASK;
/* If the name is empty, nameIndex will barf. */
if (name.indexOf(',') < 0)
return impliedMask(1 << nameIndex(name.trim()));
int mask = 0;
StringTokenizer tok = new StringTokenizer(name, ",");
while (tok.hasMoreTokens()) {
String action = tok.nextToken();
int i = nameIndex(action.trim());
mask |= (1 << i);
}
return impliedMask(mask);
}
private static int nameIndex(String name)
throws IllegalArgumentException {
for (int i = 0; i < N_NAMES; i++) {
if (names[i].equals(name))
return i;
}
final String msg =
"Invalid MBeanServerPermission name: \"" + name + "\"";
throw new IllegalArgumentException(msg);
}
public int hashCode() {
return mask;
}
/**
* <p>Checks if this MBeanServerPermission object "implies" the specified
* permission.</p>
*
* <p>More specifically, this method returns true if:</p>
*
* <ul>
* <li> <i>p</i> is an instance of MBeanServerPermission,</li>
* <li> <i>p</i>'s target names are a subset of this object's target
* names</li>
* </ul>
*
* <p>The <code>createMBeanServer</code> permission implies the
* <code>newMBeanServer</code> permission.</p>
*
* @param p the permission to check against.
* @return true if the specified permission is implied by this object,
* false if not.
*/
public boolean implies(Permission p) {
if (!(p instanceof MBeanServerPermission))
return false;
MBeanServerPermission that = (MBeanServerPermission) p;
return ((this.mask & that.mask) == that.mask);
}
/**
* Checks two MBeanServerPermission objects for equality. Checks that
* <i>obj</i> is an MBeanServerPermission, and represents the same
* list of allowable actions as this object.
* <P>
* @param obj the object we are testing for equality with this object.
* @return true if the objects are equal.
*/
public boolean equals(Object obj) {
if (obj == this)
return true;
if (! (obj instanceof MBeanServerPermission))
return false;
MBeanServerPermission that = (MBeanServerPermission) obj;
return (this.mask == that.mask);
}
public PermissionCollection newPermissionCollection() {
return new MBeanServerPermissionCollection();
}
}
/**
* Class returned by {@link MBeanServerPermission#newPermissionCollection()}.
*
* @serial include
*/
/*
* Since every collection of MBSP can be represented by a single MBSP,
* that is what our PermissionCollection does. We need to define a
* PermissionCollection because the one inherited from BasicPermission
* doesn't know that createMBeanServer implies newMBeanServer.
*
* Though the serial form is defined, the TCK does not check it. We do
* not require independent implementations to duplicate it. Even though
* PermissionCollection is Serializable, instances of this class will
* hardly ever be serialized, and different implementations do not
* typically exchange serialized permission collections.
*
* If we did require that a particular form be respected here, we would
* logically also have to require it for
* MBeanPermission.newPermissionCollection, which would preclude an
* implementation from defining a PermissionCollection there with an
* optimized "implies" method.
*/
class MBeanServerPermissionCollection extends PermissionCollection {
/** @serial Null if no permissions in collection, otherwise a
single permission that is the union of all permissions that
have been added. */
private MBeanServerPermission collectionPermission;
private static final long serialVersionUID = -5661980843569388590L;
public synchronized void add(Permission permission) {
if (!(permission instanceof MBeanServerPermission)) {
final String msg =
"Permission not an MBeanServerPermission: " + permission;
throw new IllegalArgumentException(msg);
}
if (isReadOnly())
throw new SecurityException("Read-only permission collection");
MBeanServerPermission mbsp = (MBeanServerPermission) permission;
if (collectionPermission == null)
collectionPermission = mbsp;
else if (!collectionPermission.implies(permission)) {
int newmask = collectionPermission.mask | mbsp.mask;
collectionPermission = new MBeanServerPermission(newmask);
}
}
public synchronized boolean implies(Permission permission) {
return (collectionPermission != null &&
collectionPermission.implies(permission));
}
public synchronized Enumeration<Permission> elements() {
Set<Permission> set;
if (collectionPermission == null)
set = Collections.emptySet();
else
set = Collections.singleton((Permission) collectionPermission);
return Collections.enumeration(set);
}
}