blob: f19d7217bb92ab9bd7d2abda80c186ade1102121 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.management;
27
28import java.io.IOException;
29import java.io.ObjectInputStream;
30import java.security.BasicPermission;
31import java.security.Permission;
32import java.security.PermissionCollection;
33import java.util.Collections;
34import java.util.Enumeration;
35import java.util.Set;
36import java.util.StringTokenizer;
37
38/** A Permission to perform actions related to MBeanServers.
39 The <em>name</em> of the permission specifies the operation requested
40 or granted by the permission. For a granted permission, it can be
41 <code>*</code> to allow all of the MBeanServer operations specified below.
42 Otherwise, for a granted or requested permission, it must be one of the
43 following:
44 <dl>
45 <dt>createMBeanServer</dt>
46 <dd>Create a new MBeanServer object using the method
47 {@link MBeanServerFactory#createMBeanServer()} or
48 {@link MBeanServerFactory#createMBeanServer(java.lang.String)}.
49 <dt>findMBeanServer</dt>
50 <dd>Find an MBeanServer with a given name, or all MBeanServers in this
51 JVM, using the method {@link MBeanServerFactory#findMBeanServer}.
52 <dt>newMBeanServer</dt>
53 <dd>Create a new MBeanServer object without keeping a reference to it,
54 using the method {@link MBeanServerFactory#newMBeanServer()} or
55 {@link MBeanServerFactory#newMBeanServer(java.lang.String)}.
56 <dt>releaseMBeanServer</dt>
57 <dd>Remove the MBeanServerFactory's reference to an MBeanServer,
58 using the method {@link MBeanServerFactory#releaseMBeanServer}.
59 </dl>
60 The <em>name</em> of the permission can also denote a list of one or more
61 comma-separated operations. Spaces are allowed at the beginning and
62 end of the <em>name</em> and before and after commas.
63 <p>
64 <code>MBeanServerPermission("createMBeanServer")</code> implies
65 <code>MBeanServerPermission("newMBeanServer")</code>.
66 *
67 * @since 1.5
68 */
69public class MBeanServerPermission extends BasicPermission {
70 private static final long serialVersionUID = -5661980843569388590L;
71
72 private final static int
73 CREATE = 0,
74 FIND = 1,
75 NEW = 2,
76 RELEASE = 3,
77 N_NAMES = 4;
78
79 private final static String[] names = {
80 "createMBeanServer",
81 "findMBeanServer",
82 "newMBeanServer",
83 "releaseMBeanServer",
84 };
85
86 private final static int
87 CREATE_MASK = 1<<CREATE,
88 FIND_MASK = 1<<FIND,
89 NEW_MASK = 1<<NEW,
90 RELEASE_MASK = 1<<RELEASE,
91 ALL_MASK = CREATE_MASK|FIND_MASK|NEW_MASK|RELEASE_MASK;
92
93 /*
94 * Map from permission masks to canonical names. This array is
95 * filled in on demand.
96 *
97 * This isn't very scalable. If we have more than five or six
98 * permissions, we should consider doing this differently,
99 * e.g. with a Map.
100 */
101 private final static String[] canonicalNames = new String[1 << N_NAMES];
102
103 /*
104 * The target names mask. This is not private to avoid having to
105 * generate accessor methods for accesses from the collection class.
106 *
107 * This mask includes implied bits. So if it has CREATE_MASK then
108 * it necessarily has NEW_MASK too.
109 */
110 transient int mask;
111
112 /** <p>Create a new MBeanServerPermission with the given name.</p>
113 <p>This constructor is equivalent to
114 <code>MBeanServerPermission(name,null)</code>.</p>
115 @param name the name of the granted permission. It must
116 respect the constraints spelt out in the description of the
117 {@link MBeanServerPermission} class.
118 @exception NullPointerException if the name is null.
119 @exception IllegalArgumentException if the name is not
120 <code>*</code> or one of the allowed names or a comma-separated
121 list of the allowed names.
122 */
123 public MBeanServerPermission(String name) {
124 this(name, null);
125 }
126
127 /** <p>Create a new MBeanServerPermission with the given name.</p>
128 @param name the name of the granted permission. It must
129 respect the constraints spelt out in the description of the
130 {@link MBeanServerPermission} class.
131 @param actions the associated actions. This parameter is not
132 currently used and must be null or the empty string.
133 @exception NullPointerException if the name is null.
134 @exception IllegalArgumentException if the name is not
135 <code>*</code> or one of the allowed names or a comma-separated
136 list of the allowed names, or if <code>actions</code> is a non-null
137 non-empty string.
138 *
139 * @throws NullPointerException if <code>name</code> is <code>null</code>.
140 * @throws IllegalArgumentException if <code>name</code> is empty or
141 * if arguments are invalid.
142 */
143 public MBeanServerPermission(String name, String actions) {
144 super(getCanonicalName(parseMask(name)), actions);
145
146 /* It's annoying to have to parse the name twice, but since
147 Permission.getName() is final and since we can't access "this"
148 until after the call to the superclass constructor, there
149 isn't any very clean way to do this. MBeanServerPermission
150 objects aren't constructed very often, luckily. */
151 mask = parseMask(name);
152
153 /* Check that actions is a null empty string */
154 if (actions != null && actions.length() > 0)
155 throw new IllegalArgumentException("MBeanServerPermission " +
156 "actions must be null: " +
157 actions);
158 }
159
160 MBeanServerPermission(int mask) {
161 super(getCanonicalName(mask));
162 this.mask = impliedMask(mask);
163 }
164
165 private void readObject(ObjectInputStream in)
166 throws IOException, ClassNotFoundException {
167 in.defaultReadObject();
168 mask = parseMask(getName());
169 }
170
171 static int simplifyMask(int mask) {
172 if ((mask & CREATE_MASK) != 0)
173 mask &= ~NEW_MASK;
174 return mask;
175 }
176
177 static int impliedMask(int mask) {
178 if ((mask & CREATE_MASK) != 0)
179 mask |= NEW_MASK;
180 return mask;
181 }
182
183 static String getCanonicalName(int mask) {
184 if (mask == ALL_MASK)
185 return "*";
186
187 mask = simplifyMask(mask);
188
189 synchronized (canonicalNames) {
190 if (canonicalNames[mask] == null)
191 canonicalNames[mask] = makeCanonicalName(mask);
192 }
193
194 return canonicalNames[mask];
195 }
196
197 private static String makeCanonicalName(int mask) {
198 final StringBuilder buf = new StringBuilder();
199 for (int i = 0; i < N_NAMES; i++) {
200 if ((mask & (1<<i)) != 0) {
201 if (buf.length() > 0)
202 buf.append(',');
203 buf.append(names[i]);
204 }
205 }
206 return buf.toString().intern();
207 /* intern() avoids duplication when the mask has only
208 one bit, so is equivalent to the string constants
209 we have for the names[] array. */
210 }
211
212 /* Convert the string into a bitmask, including bits that
213 are implied by the permissions in the string. */
214 private static int parseMask(String name) {
215 /* Check that target name is a non-null non-empty string */
216 if (name == null) {
217 throw new NullPointerException("MBeanServerPermission: " +
218 "target name can't be null");
219 }
220
221 name = name.trim();
222 if (name.equals("*"))
223 return ALL_MASK;
224
225 /* If the name is empty, nameIndex will barf. */
226 if (name.indexOf(',') < 0)
227 return impliedMask(1 << nameIndex(name.trim()));
228
229 int mask = 0;
230
231 StringTokenizer tok = new StringTokenizer(name, ",");
232 while (tok.hasMoreTokens()) {
233 String action = tok.nextToken();
234 int i = nameIndex(action.trim());
235 mask |= (1 << i);
236 }
237
238 return impliedMask(mask);
239 }
240
241 private static int nameIndex(String name)
242 throws IllegalArgumentException {
243 for (int i = 0; i < N_NAMES; i++) {
244 if (names[i].equals(name))
245 return i;
246 }
247 final String msg =
248 "Invalid MBeanServerPermission name: \"" + name + "\"";
249 throw new IllegalArgumentException(msg);
250 }
251
252 public int hashCode() {
253 return mask;
254 }
255
256 /**
257 * <p>Checks if this MBeanServerPermission object "implies" the specified
258 * permission.</p>
259 *
260 * <p>More specifically, this method returns true if:</p>
261 *
262 * <ul>
263 * <li> <i>p</i> is an instance of MBeanServerPermission,</li>
264 * <li> <i>p</i>'s target names are a subset of this object's target
265 * names</li>
266 * </ul>
267 *
268 * <p>The <code>createMBeanServer</code> permission implies the
269 * <code>newMBeanServer</code> permission.</p>
270 *
271 * @param p the permission to check against.
272 * @return true if the specified permission is implied by this object,
273 * false if not.
274 */
275 public boolean implies(Permission p) {
276 if (!(p instanceof MBeanServerPermission))
277 return false;
278
279 MBeanServerPermission that = (MBeanServerPermission) p;
280
281 return ((this.mask & that.mask) == that.mask);
282 }
283
284 /**
285 * Checks two MBeanServerPermission objects for equality. Checks that
286 * <i>obj</i> is an MBeanServerPermission, and represents the same
287 * list of allowable actions as this object.
288 * <P>
289 * @param obj the object we are testing for equality with this object.
290 * @return true if the objects are equal.
291 */
292 public boolean equals(Object obj) {
293 if (obj == this)
294 return true;
295
296 if (! (obj instanceof MBeanServerPermission))
297 return false;
298
299 MBeanServerPermission that = (MBeanServerPermission) obj;
300
301 return (this.mask == that.mask);
302 }
303
304 public PermissionCollection newPermissionCollection() {
305 return new MBeanServerPermissionCollection();
306 }
307}
308
309/**
310 * Class returned by {@link MBeanServerPermission#newPermissionCollection()}.
311 *
312 * @serial include
313 */
314
315/*
316 * Since every collection of MBSP can be represented by a single MBSP,
317 * that is what our PermissionCollection does. We need to define a
318 * PermissionCollection because the one inherited from BasicPermission
319 * doesn't know that createMBeanServer implies newMBeanServer.
320 *
321 * Though the serial form is defined, the TCK does not check it. We do
322 * not require independent implementations to duplicate it. Even though
323 * PermissionCollection is Serializable, instances of this class will
324 * hardly ever be serialized, and different implementations do not
325 * typically exchange serialized permission collections.
326 *
327 * If we did require that a particular form be respected here, we would
328 * logically also have to require it for
329 * MBeanPermission.newPermissionCollection, which would preclude an
330 * implementation from defining a PermissionCollection there with an
331 * optimized "implies" method.
332 */
333class MBeanServerPermissionCollection extends PermissionCollection {
334 /** @serial Null if no permissions in collection, otherwise a
335 single permission that is the union of all permissions that
336 have been added. */
337 private MBeanServerPermission collectionPermission;
338
339 private static final long serialVersionUID = -5661980843569388590L;
340
341 public synchronized void add(Permission permission) {
342 if (!(permission instanceof MBeanServerPermission)) {
343 final String msg =
344 "Permission not an MBeanServerPermission: " + permission;
345 throw new IllegalArgumentException(msg);
346 }
347 if (isReadOnly())
348 throw new SecurityException("Read-only permission collection");
349 MBeanServerPermission mbsp = (MBeanServerPermission) permission;
350 if (collectionPermission == null)
351 collectionPermission = mbsp;
352 else if (!collectionPermission.implies(permission)) {
353 int newmask = collectionPermission.mask | mbsp.mask;
354 collectionPermission = new MBeanServerPermission(newmask);
355 }
356 }
357
358 public synchronized boolean implies(Permission permission) {
359 return (collectionPermission != null &&
360 collectionPermission.implies(permission));
361 }
362
363 public synchronized Enumeration<Permission> elements() {
364 Set<Permission> set;
365 if (collectionPermission == null)
366 set = Collections.emptySet();
367 else
368 set = Collections.singleton((Permission) collectionPermission);
369 return Collections.enumeration(set);
370 }
371}