1 /*
2 * Copyright 1999-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
26 package javax.management;
27
28 import static com.sun.jmx.defaults.JmxProperties.JMX_INITIAL_BUILDER;
29 import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
30 import com.sun.jmx.interceptor.DefaultMBeanServerInterceptor;
31 import com.sun.jmx.mbeanserver.GetPropertyAction;
32 import java.security.AccessController;
33 import java.security.Permission;
34 import java.util.ArrayList;
35 import java.util.logging.Level;
36 import javax.management.loading.ClassLoaderRepository;
37
38 /**
39 * <p>Provides MBean server references. There are no instances of
40 * this class.</p>
41 *
42 * <p>Since JMX 1.2 this class makes it possible to replace the default
43 * MBeanServer implementation. This is done using the
44 * {@link javax.management.MBeanServerBuilder} class.
45 * The class of the initial MBeanServerBuilder to be
46 * instantiated can be specified through the
47 * <b>javax.management.builder.initial</b> system property.
48 * The specified class must be a public subclass of
49 * {@link javax.management.MBeanServerBuilder}, and must have a public
50 * empty constructor.
51 * <p>By default, if no value for that property is specified, an instance of
52 * {@link
53 * javax.management.MBeanServerBuilder javax.management.MBeanServerBuilder}
54 * is created. Otherwise, the MBeanServerFactory attempts to load the
55 * specified class using
56 * {@link java.lang.Thread#getContextClassLoader()
57 * Thread.currentThread().getContextClassLoader()}, or if that is null,
58 * {@link java.lang.Class#forName(java.lang.String) Class.forName()}. Then
59 * it creates an initial instance of that Class using
60 * {@link java.lang.Class#newInstance()}. If any checked exception
61 * is raised during this process (e.g.
62 * {@link java.lang.ClassNotFoundException},
63 * {@link java.lang.InstantiationException}) the MBeanServerFactory
64 * will propagate this exception from within a RuntimeException.</p>
65 *
66 * <p>The <b>javax.management.builder.initial</b> system property is
67 * consulted every time a new MBeanServer needs to be created, and the
68 * class pointed to by that property is loaded. If that class is different
69 * from that of the current MBeanServerBuilder, then a new MBeanServerBuilder
70 * is created. Otherwise, the MBeanServerFactory may create a new
71 * MBeanServerBuilder or reuse the current one.</p>
72 *
73 * <p>If the class pointed to by the property cannot be
74 * loaded, or does not correspond to a valid subclass of MBeanServerBuilder
75 * then an exception is propagated, and no MBeanServer can be created until
76 * the <b>javax.management.builder.initial</b> system property is reset to
77 * valid value.</p>
78 *
79 * <p>The MBeanServerBuilder makes it possible to wrap the MBeanServers
80 * returned by the default MBeanServerBuilder implementation, for the purpose
81 * of e.g. adding an additional security layer.</p>
82 *
83 * @since 1.5
84 */
85 public class MBeanServerFactory {
86
87 /*
88 * There are no instances of this class so don't generate the
89 * default public constructor.
90 */
91 private MBeanServerFactory() {
92
93 }
94
95 /**
96 * The builder that will be used to construct MBeanServers.
97 *
98 **/
99 private static MBeanServerBuilder builder = null;
100
101 /**
102 * Provide a new {@link javax.management.MBeanServerBuilder}.
103 * @param builder The new MBeanServerBuilder that will be used to
104 * create {@link javax.management.MBeanServer}s.
105 * @exception IllegalArgumentException if the given builder is null.
106 *
107 * @exception SecurityException if there is a SecurityManager and
108 * the caller's permissions do not include or imply <code>{@link
109 * MBeanServerPermission}("setMBeanServerBuilder")</code>.
110 *
111 **/
112 // public static synchronized void
113 // setMBeanServerBuilder(MBeanServerBuilder builder) {
114 // checkPermission("setMBeanServerBuilder");
115 // MBeanServerFactory.builder = builder;
116 // }
117
118 /**
119 * Get the current {@link javax.management.MBeanServerBuilder}.
120 *
121 * @return the current {@link javax.management.MBeanServerBuilder}.
122 *
123 * @exception SecurityException if there is a SecurityManager and
124 * the caller's permissions do not include or imply <code>{@link
125 * MBeanServerPermission}("getMBeanServerBuilder")</code>.
126 *
127 **/
128 // public static synchronized MBeanServerBuilder getMBeanServerBuilder() {
129 // checkPermission("getMBeanServerBuilder");
130 // return builder;
131 // }
132
133 /**
134 * Remove internal MBeanServerFactory references to a created
135 * MBeanServer. This allows the garbage collector to remove the
136 * MBeanServer object.
137 *
138 * @param mbeanServer the MBeanServer object to remove.
139 *
140 * @exception java.lang.IllegalArgumentException if
141 * <code>mbeanServer</code> was not generated by one of the
142 * <code>createMBeanServer</code> methods, or if
143 * <code>releaseMBeanServer</code> was already called on it.
144 *
145 * @exception SecurityException if there is a SecurityManager and
146 * the caller's permissions do not include or imply <code>{@link
147 * MBeanServerPermission}("releaseMBeanServer")</code>.
148 */
149 public static void releaseMBeanServer(MBeanServer mbeanServer) {
150 checkPermission("releaseMBeanServer");
151
152 removeMBeanServer(mbeanServer);
153 }
154
155 /**
156 * <p>Return a new object implementing the MBeanServer interface
157 * with a standard default domain name. The default domain name
158 * is used as the domain part in the ObjectName of MBeans when the
159 * domain is specified by the user is null.</p>
160 *
161 * <p>The standard default domain name is
162 * <code>DefaultDomain</code>.</p>
163 *
164 * <p>The MBeanServer reference is internally kept. This will
165 * allow <CODE>findMBeanServer</CODE> to return a reference to
166 * this MBeanServer object.</p>
167 *
168 * <p>This method is equivalent to <code>createMBeanServer(null)</code>.
169 *
170 * @return the newly created MBeanServer.
171 *
172 * @exception SecurityException if there is a SecurityManager and the
173 * caller's permissions do not include or imply <code>{@link
174 * MBeanServerPermission}("createMBeanServer")</code>.
175 *
176 * @exception JMRuntimeException if the property
177 * <code>javax.management.builder.initial</code> exists but the
178 * class it names cannot be instantiated through a public
179 * no-argument constructor; or if the instantiated builder returns
180 * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
181 * newMBeanServerDelegate} or {@link
182 * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
183 *
184 * @exception ClassCastException if the property
185 * <code>javax.management.builder.initial</code> exists and can be
186 * instantiated but is not assignment compatible with {@link
187 * MBeanServerBuilder}.
188 */
189 public static MBeanServer createMBeanServer() {
190 return createMBeanServer(null);
191 }
192
193 /**
194 * <p>Return a new object implementing the {@link MBeanServer}
195 * interface with the specified default domain name. The given
196 * domain name is used as the domain part in the ObjectName of
197 * MBeans when the domain is specified by the user is null.</p>
198 *
199 * <p>The MBeanServer reference is internally kept. This will
200 * allow <CODE>findMBeanServer</CODE> to return a reference to
201 * this MBeanServer object.</p>
202 *
203 * @param domain the default domain name for the created
204 * MBeanServer. This is the value that will be returned by {@link
205 * MBeanServer#getDefaultDomain}.
206 *
207 * @return the newly created MBeanServer.
208 *
209 * @exception SecurityException if there is a SecurityManager and
210 * the caller's permissions do not include or imply <code>{@link
211 * MBeanServerPermission}("createMBeanServer")</code>.
212 *
213 * @exception JMRuntimeException if the property
214 * <code>javax.management.builder.initial</code> exists but the
215 * class it names cannot be instantiated through a public
216 * no-argument constructor; or if the instantiated builder returns
217 * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
218 * newMBeanServerDelegate} or {@link
219 * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
220 *
221 * @exception ClassCastException if the property
222 * <code>javax.management.builder.initial</code> exists and can be
223 * instantiated but is not assignment compatible with {@link
224 * MBeanServerBuilder}.
225 */
226 public static MBeanServer createMBeanServer(String domain) {
227 checkPermission("createMBeanServer");
228
229 final MBeanServer mBeanServer = newMBeanServer(domain);
230 addMBeanServer(mBeanServer);
231 return mBeanServer;
232 }
233
234 /**
235 * <p>Return a new object implementing the MBeanServer interface
236 * with a standard default domain name, without keeping an
237 * internal reference to this new object. The default domain name
238 * is used as the domain part in the ObjectName of MBeans when the
239 * domain is specified by the user is null.</p>
240 *
241 * <p>The standard default domain name is
242 * <code>DefaultDomain</code>.</p>
243 *
244 * <p>No reference is kept. <CODE>findMBeanServer</CODE> will not
245 * be able to return a reference to this MBeanServer object, but
246 * the garbage collector will be able to remove the MBeanServer
247 * object when it is no longer referenced.</p>
248 *
249 * <p>This method is equivalent to <code>newMBeanServer(null)</code>.</p>
250 *
251 * @return the newly created MBeanServer.
252 *
253 * @exception SecurityException if there is a SecurityManager and the
254 * caller's permissions do not include or imply <code>{@link
255 * MBeanServerPermission}("newMBeanServer")</code>.
256 *
257 * @exception JMRuntimeException if the property
258 * <code>javax.management.builder.initial</code> exists but the
259 * class it names cannot be instantiated through a public
260 * no-argument constructor; or if the instantiated builder returns
261 * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
262 * newMBeanServerDelegate} or {@link
263 * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
264 *
265 * @exception ClassCastException if the property
266 * <code>javax.management.builder.initial</code> exists and can be
267 * instantiated but is not assignment compatible with {@link
268 * MBeanServerBuilder}.
269 */
270 public static MBeanServer newMBeanServer() {
271 return newMBeanServer(null);
272 }
273
274 /**
275 * <p>Return a new object implementing the MBeanServer interface
276 * with the specified default domain name, without keeping an
277 * internal reference to this new object. The given domain name
278 * is used as the domain part in the ObjectName of MBeans when the
279 * domain is specified by the user is null.</p>
280 *
281 * <p>No reference is kept. <CODE>findMBeanServer</CODE> will not
282 * be able to return a reference to this MBeanServer object, but
283 * the garbage collector will be able to remove the MBeanServer
284 * object when it is no longer referenced.</p>
285 *
286 * @param domain the default domain name for the created
287 * MBeanServer. This is the value that will be returned by {@link
288 * MBeanServer#getDefaultDomain}.
289 *
290 * @return the newly created MBeanServer.
291 *
292 * @exception SecurityException if there is a SecurityManager and the
293 * caller's permissions do not include or imply <code>{@link
294 * MBeanServerPermission}("newMBeanServer")</code>.
295 *
296 * @exception JMRuntimeException if the property
297 * <code>javax.management.builder.initial</code> exists but the
298 * class it names cannot be instantiated through a public
299 * no-argument constructor; or if the instantiated builder returns
300 * null from its {@link MBeanServerBuilder#newMBeanServerDelegate
301 * newMBeanServerDelegate} or {@link
302 * MBeanServerBuilder#newMBeanServer newMBeanServer} methods.
303 *
304 * @exception ClassCastException if the property
305 * <code>javax.management.builder.initial</code> exists and can be
306 * instantiated but is not assignment compatible with {@link
307 * MBeanServerBuilder}.
308 */
309 public static MBeanServer newMBeanServer(String domain) {
310 checkPermission("newMBeanServer");
311
312 // Get the builder. Creates a new one if necessary.
313 //
314 final MBeanServerBuilder mbsBuilder = getNewMBeanServerBuilder();
315 // Returned value cannot be null. NullPointerException if violated.
316
317 synchronized(mbsBuilder) {
318 final MBeanServerDelegate delegate =
319 mbsBuilder.newMBeanServerDelegate();
320 if (delegate == null) {
321 final String msg =
322 "MBeanServerBuilder.newMBeanServerDelegate() " +
323 "returned null";
324 throw new JMRuntimeException(msg);
325 }
326 final MBeanServer mbeanServer =
327 mbsBuilder.newMBeanServer(domain,null,delegate);
328 if (mbeanServer == null) {
329 final String msg =
330 "MBeanServerBuilder.newMBeanServer() returned null";
331 throw new JMRuntimeException(msg);
332 }
333 return mbeanServer;
334 }
335 }
336
337 /**
338 * <p>Return a list of registered MBeanServer objects. A
339 * registered MBeanServer object is one that was created by one of
340 * the <code>createMBeanServer</code> methods and not subsequently
341 * released with <code>releaseMBeanServer</code>.</p>
342 *
343 * @param agentId The agent identifier of the MBeanServer to
344 * retrieve. If this parameter is null, all registered
345 * MBeanServers in this JVM are returned. Otherwise, only
346 * MBeanServers whose id is equal to <code>agentId</code> are
347 * returned. The id of an MBeanServer is the
348 * <code>MBeanServerId</code> attribute of its delegate MBean.
349 *
350 * @return A list of MBeanServer objects.
351 *
352 * @exception SecurityException if there is a SecurityManager and the
353 * caller's permissions do not include or imply <code>{@link
354 * MBeanServerPermission}("findMBeanServer")</code>.
355 */
356 public synchronized static
357 ArrayList<MBeanServer> findMBeanServer(String agentId) {
358
359 checkPermission("findMBeanServer");
360
361 if (agentId == null)
362 return new ArrayList<MBeanServer>(mBeanServerList);
363
364 ArrayList<MBeanServer> result = new ArrayList<MBeanServer>();
365 for (MBeanServer mbs : mBeanServerList) {
366 String name = mBeanServerName(mbs);
367 if (agentId.equals(name))
368 result.add(mbs);
369 }
370 return result;
371 }
372
373 /**
374 * Return the ClassLoaderRepository used by the given MBeanServer.
375 * This method is equivalent to {@link MBeanServer#getClassLoaderRepository() server.getClassLoaderRepository()}.
376 * @param server The MBeanServer under examination. Since JMX 1.2,
377 * if <code>server</code> is <code>null</code>, the result is a
378 * {@link NullPointerException}. This behavior differs from what
379 * was implemented in JMX 1.1 - where the possibility to use
380 * <code>null</code> was deprecated.
381 * @return The Class Loader Repository used by the given MBeanServer.
382 * @exception SecurityException if there is a SecurityManager and
383 * the caller's permissions do not include or imply <code>{@link
384 * MBeanPermission}("getClassLoaderRepository")</code>.
385 *
386 * @exception NullPointerException if <code>server</code> is null.
387 *
388 **/
389 public static ClassLoaderRepository getClassLoaderRepository(
390 MBeanServer server) {
391 return server.getClassLoaderRepository();
392 }
393
394 private static String mBeanServerName(MBeanServer mbs) {
395 try {
396 return (String) mbs.getAttribute(MBeanServerDelegate.DELEGATE_NAME,
397 "MBeanServerId");
398 } catch (JMException e) {
399 return null;
400 }
401 }
402
403 private static void checkPermission(String action)
404 throws SecurityException {
405 SecurityManager sm = System.getSecurityManager();
406 if (sm != null) {
407 Permission perm = new MBeanServerPermission(action);
408 sm.checkPermission(perm);
409 }
410 }
411
412 private static synchronized void addMBeanServer(MBeanServer mbs) {
413 mBeanServerList.add(mbs);
414 }
415
416 private static synchronized void removeMBeanServer(MBeanServer mbs) {
417 boolean removed = mBeanServerList.remove(mbs);
418 if (!removed) {
419 MBEANSERVER_LOGGER.logp(Level.FINER,
420 MBeanServerFactory.class.getName(),
421 "removeMBeanServer(MBeanServer)",
422 "MBeanServer was not in list!");
423 throw new IllegalArgumentException("MBeanServer was not in list!");
424 }
425 }
426
427 private static final ArrayList<MBeanServer> mBeanServerList =
428 new ArrayList<MBeanServer>();
429
430 /**
431 * Load the builder class through the context class loader.
432 * @param builderClassName The name of the builder class.
433 **/
434 private static Class loadBuilderClass(String builderClassName)
435 throws ClassNotFoundException {
436 final ClassLoader loader =
437 Thread.currentThread().getContextClassLoader();
438
439 if (loader != null) {
440 // Try with context class loader
441 return loader.loadClass(builderClassName);
442 }
443
444 // No context class loader? Try with Class.forName()
445 return Class.forName(builderClassName);
446 }
447
448 /**
449 * Creates the initial builder according to the
450 * javax.management.builder.initial System property - if specified.
451 * If any checked exception needs to be thrown, it is embedded in
452 * a JMRuntimeException.
453 **/
454 private static MBeanServerBuilder newBuilder(Class builderClass) {
455 try {
456 final Object builder = builderClass.newInstance();
457 return (MBeanServerBuilder)builder;
458 } catch (RuntimeException x) {
459 throw x;
460 } catch (Exception x) {
461 final String msg =
462 "Failed to instantiate a MBeanServerBuilder from " +
463 builderClass + ": " + x;
464 throw new JMRuntimeException(msg, x);
465 }
466 }
467
468 /**
469 * Instantiate a new builder according to the
470 * javax.management.builder.initial System property - if needed.
471 **/
472 private static synchronized void checkMBeanServerBuilder() {
473 try {
474 GetPropertyAction act =
475 new GetPropertyAction(JMX_INITIAL_BUILDER);
476 String builderClassName = AccessController.doPrivileged(act);
477
478 try {
479 final Class newBuilderClass;
480 if (builderClassName == null || builderClassName.length() == 0)
481 newBuilderClass = MBeanServerBuilder.class;
482 else
483 newBuilderClass = loadBuilderClass(builderClassName);
484
485 // Check whether a new builder needs to be created
486 if (builder != null) {
487 final Class builderClass = builder.getClass();
488 if (newBuilderClass == builderClass)
489 return; // no need to create a new builder...
490 }
491
492 // Create a new builder
493 builder = newBuilder(newBuilderClass);
494 } catch (ClassNotFoundException x) {
495 final String msg =
496 "Failed to load MBeanServerBuilder class " +
497 builderClassName + ": " + x;
498 throw new JMRuntimeException(msg, x);
499 }
500 } catch (RuntimeException x) {
501 if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) {
502 StringBuilder strb = new StringBuilder()
503 .append("Failed to instantiate MBeanServerBuilder: ").append(x)
504 .append("\n\t\tCheck the value of the ")
505 .append(JMX_INITIAL_BUILDER).append(" property.");
506 MBEANSERVER_LOGGER.logp(Level.FINEST,
507 MBeanServerFactory.class.getName(),
508 "checkMBeanServerBuilder",
509 strb.toString());
510 }
511 throw x;
512 }
513 }
514
515 /**
516 * Get the current {@link javax.management.MBeanServerBuilder},
517 * as specified by the current value of the
518 * javax.management.builder.initial property.
519 *
520 * This method consults the property and instantiates a new builder
521 * if needed.
522 *
523 * @return the new current {@link javax.management.MBeanServerBuilder}.
524 *
525 * @exception SecurityException if there is a SecurityManager and
526 * the caller's permissions do not make it possible to instantiate
527 * a new builder.
528 * @exception JMRuntimeException if the builder instantiation
529 * fails with a checked exception -
530 * {@link java.lang.ClassNotFoundException} etc...
531 *
532 **/
533 private static synchronized MBeanServerBuilder getNewMBeanServerBuilder() {
534 checkMBeanServerBuilder();
535 return builder;
536 }
537
538 }