blob: a637aa14745e1b46923cd7dc995c0f5e4469ccb7 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-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 com.sun.jmx.interceptor;
27
28// java import
29import java.util.ArrayList;
30import java.util.Iterator;
31import java.util.List;
32import java.util.logging.Level;
33import java.util.Set;
34import java.util.HashSet;
35import java.util.WeakHashMap;
36import java.lang.ref.WeakReference;
37import java.io.PrintWriter;
38import java.io.StringWriter;
39import java.security.AccessControlContext;
40import java.security.Permission;
41import java.security.ProtectionDomain;
42import java.security.AccessController;
43import java.security.PrivilegedAction;
44
45// JMX import
46import javax.management.Attribute;
47import javax.management.AttributeList;
48import javax.management.AttributeNotFoundException;
49import javax.management.DynamicMBean;
50import javax.management.InstanceAlreadyExistsException;
51import javax.management.InstanceNotFoundException;
52import javax.management.IntrospectionException;
53import javax.management.InvalidAttributeValueException;
54import javax.management.JMException;
55import javax.management.JMRuntimeException;
56import javax.management.ListenerNotFoundException;
57import javax.management.MalformedObjectNameException;
58import javax.management.MBeanException;
59import javax.management.MBeanInfo;
60import javax.management.MBeanPermission;
61import javax.management.MBeanRegistration;
62import javax.management.MBeanRegistrationException;
63import javax.management.MBeanServer;
64import javax.management.MBeanServerDelegate;
65import javax.management.MBeanServerNotification;
66import javax.management.MBeanTrustPermission;
67import javax.management.NotCompliantMBeanException;
68import javax.management.Notification;
69import javax.management.NotificationBroadcaster;
70import javax.management.NotificationEmitter;
71import javax.management.NotificationFilter;
72import javax.management.NotificationListener;
73import javax.management.ObjectInstance;
74import javax.management.ObjectName;
75import javax.management.QueryEval;
76import javax.management.QueryExp;
77import javax.management.ReflectionException;
78import javax.management.RuntimeErrorException;
79import javax.management.RuntimeMBeanException;
80import javax.management.RuntimeOperationsException;
81
82// JMX RI
83import static com.sun.jmx.defaults.JmxProperties.MBEANSERVER_LOGGER;
84import com.sun.jmx.mbeanserver.DynamicMBean2;
85import com.sun.jmx.mbeanserver.ModifiableClassLoaderRepository;
86import com.sun.jmx.mbeanserver.MBeanInstantiator;
87import com.sun.jmx.mbeanserver.MXBeanSupport;
88import com.sun.jmx.mbeanserver.Repository;
89import com.sun.jmx.mbeanserver.NamedObject;
90import com.sun.jmx.defaults.ServiceName;
91import com.sun.jmx.mbeanserver.Introspector;
92import com.sun.jmx.remote.util.EnvHelp;
93
94/**
95 * This is the default class for MBean manipulation on the agent side. It
96 * contains the methods necessary for the creation, registration, and
97 * deletion of MBeans as well as the access methods for registered MBeans.
98 * This is the core component of the JMX infrastructure.
99 * <P>
100 * Every MBean which is added to the MBean server becomes manageable: its attributes and operations
101 * become remotely accessible through the connectors/adaptors connected to that MBean server.
102 * A Java object cannot be registered in the MBean server unless it is a JMX compliant MBean.
103 * <P>
104 * When an MBean is registered or unregistered in the MBean server an
105 * {@link javax.management.MBeanServerNotification MBeanServerNotification}
106 * Notification is emitted. To register an object as listener to MBeanServerNotifications
107 * you should call the MBean server method {@link #addNotificationListener addNotificationListener} with <CODE>ObjectName</CODE>
108 * the <CODE>ObjectName</CODE> of the {@link javax.management.MBeanServerDelegate MBeanServerDelegate}.
109 * This <CODE>ObjectName</CODE> is:
110 * <BR>
111 * <CODE>JMImplementation:type=MBeanServerDelegate</CODE>.
112 *
113 * @since 1.5
114 */
115public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
116
117 /** The MBeanInstantiator object used by the
118 * DefaultMBeanServerInterceptor */
119 private final transient MBeanInstantiator instantiator;
120
121 /** The MBean server object that is associated to the
122 * DefaultMBeanServerInterceptor */
123 private transient MBeanServer server = null;
124
125 /** The MBean server object taht associated to the
126 * DefaultMBeanServerInterceptor */
127 private final transient MBeanServerDelegate delegate;
128
129 /** The Repository object used by the DefaultMBeanServerInterceptor */
130 private final transient Repository repository;
131
132 /** Wrappers for client listeners. */
133 /* See the comment before addNotificationListener below. */
134 private final transient
135 WeakHashMap<ListenerWrapper, WeakReference<ListenerWrapper>>
136 listenerWrappers =
137 new WeakHashMap<ListenerWrapper,
138 WeakReference<ListenerWrapper>>();
139
140 /** The default domain of the object names */
141 private final String domain;
142
143 /** True if the repository perform queries, false otherwise */
144 private boolean queryByRepo;
145
146 /** The sequence number identifyng the notifications sent */
147 // Now sequence number is handled by MBeanServerDelegate.
148 // private int sequenceNumber=0;
149
150 /**
151 * Creates a DefaultMBeanServerInterceptor with the specified
152 * repository instance.
153 * <p>Do not forget to call <code>initialize(outer,delegate)</code>
154 * before using this object.
155 * @param outer A pointer to the MBeanServer object that must be
156 * passed to the MBeans when invoking their
157 * {@link javax.management.MBeanRegistration} interface.
158 * @param delegate A pointer to the MBeanServerDelegate associated
159 * with the new MBeanServer. The new MBeanServer must register
160 * this MBean in its MBean repository.
161 * @param instantiator The MBeanInstantiator that will be used to
162 * instantiate MBeans and take care of class loading issues.
163 * @param repository The repository to use for this MBeanServer.
164 */
165 public DefaultMBeanServerInterceptor(MBeanServer outer,
166 MBeanServerDelegate delegate,
167 MBeanInstantiator instantiator,
168 Repository repository) {
169 if (outer == null) throw new
170 IllegalArgumentException("outer MBeanServer cannot be null");
171 if (delegate == null) throw new
172 IllegalArgumentException("MBeanServerDelegate cannot be null");
173 if (instantiator == null) throw new
174 IllegalArgumentException("MBeanInstantiator cannot be null");
175 if (repository == null) throw new
176 IllegalArgumentException("Repository cannot be null");
177
178 this.server = outer;
179 this.delegate = delegate;
180 this.instantiator = instantiator;
181 this.repository = repository;
182 this.domain = repository.getDefaultDomain();
183 }
184
185 public ObjectInstance createMBean(String className, ObjectName name)
186 throws ReflectionException, InstanceAlreadyExistsException,
187 MBeanRegistrationException, MBeanException,
188 NotCompliantMBeanException {
189
190 return createMBean(className, name, (Object[]) null, (String[]) null);
191
192 }
193
194 public ObjectInstance createMBean(String className, ObjectName name,
195 ObjectName loaderName)
196 throws ReflectionException, InstanceAlreadyExistsException,
197 MBeanRegistrationException, MBeanException,
198 NotCompliantMBeanException, InstanceNotFoundException {
199
200 return createMBean(className, name, loaderName, (Object[]) null,
201 (String[]) null);
202 }
203
204 public ObjectInstance createMBean(String className, ObjectName name,
205 Object[] params, String[] signature)
206 throws ReflectionException, InstanceAlreadyExistsException,
207 MBeanRegistrationException, MBeanException,
208 NotCompliantMBeanException {
209
210 try {
211 return createMBean(className, name, null, true,
212 params, signature);
213 } catch (InstanceNotFoundException e) {
214 /* Can only happen if loaderName doesn't exist, but we just
215 passed null, so we shouldn't get this exception. */
216 throw EnvHelp.initCause(
217 new IllegalArgumentException("Unexpected exception: " + e), e);
218 }
219 }
220
221 public ObjectInstance createMBean(String className, ObjectName name,
222 ObjectName loaderName,
223 Object[] params, String[] signature)
224 throws ReflectionException, InstanceAlreadyExistsException,
225 MBeanRegistrationException, MBeanException,
226 NotCompliantMBeanException, InstanceNotFoundException {
227
228 return createMBean(className, name, loaderName, false,
229 params, signature);
230 }
231
232 private ObjectInstance createMBean(String className, ObjectName name,
233 ObjectName loaderName,
234 boolean withDefaultLoaderRepository,
235 Object[] params, String[] signature)
236 throws ReflectionException, InstanceAlreadyExistsException,
237 MBeanRegistrationException, MBeanException,
238 NotCompliantMBeanException, InstanceNotFoundException {
239
240 ObjectName logicalName = name;
241 Class theClass;
242
243 if (className == null) {
244 final RuntimeException wrapped =
245 new IllegalArgumentException("The class name cannot be null");
246 throw new RuntimeOperationsException(wrapped,
247 "Exception occurred during MBean creation");
248 }
249
250 if (name != null) {
251 if (name.isPattern()) {
252 final RuntimeException wrapped =
253 new IllegalArgumentException("Invalid name->" +
254 name.toString());
255 final String msg = "Exception occurred during MBean creation";
256 throw new RuntimeOperationsException(wrapped, msg);
257 }
258
259 name = nonDefaultDomain(name);
260 }
261
262 checkMBeanPermission(className, null, null, "instantiate");
263 checkMBeanPermission(className, null, name, "registerMBean");
264
265 /* Load the appropriate class. */
266 if (withDefaultLoaderRepository) {
267 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
268 MBEANSERVER_LOGGER.logp(Level.FINER,
269 DefaultMBeanServerInterceptor.class.getName(),
270 "createMBean",
271 "ClassName = " + className + ", ObjectName = " + name);
272 }
273 theClass =
274 instantiator.findClassWithDefaultLoaderRepository(className);
275 } else if (loaderName == null) {
276 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
277 MBEANSERVER_LOGGER.logp(Level.FINER,
278 DefaultMBeanServerInterceptor.class.getName(),
279 "createMBean", "ClassName = " + className +
280 ", ObjectName = " + name + ", Loader name = null");
281 }
282
283 theClass = instantiator.findClass(className,
284 server.getClass().getClassLoader());
285 } else {
286 loaderName = nonDefaultDomain(loaderName);
287
288 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
289 MBEANSERVER_LOGGER.logp(Level.FINER,
290 DefaultMBeanServerInterceptor.class.getName(),
291 "createMBean", "ClassName = " + className +
292 ", ObjectName = " + name +
293 ", Loader name = " + loaderName);
294 }
295
296 theClass = instantiator.findClass(className, loaderName);
297 }
298
299 checkMBeanTrustPermission(theClass);
300
301 // Check that the MBean can be instantiated by the MBeanServer.
302 Introspector.testCreation(theClass);
303
304 // Check the JMX MBean compliance of the class
305 Introspector.checkCompliance(theClass);
306
307 Object moi= instantiator.instantiate(theClass, params, signature,
308 server.getClass().getClassLoader());
309
310 final String infoClassName = getNewMBeanClassName(moi);
311
312 return registerObject(infoClassName, moi, name);
313 }
314
315 public ObjectInstance registerMBean(Object object, ObjectName name)
316 throws InstanceAlreadyExistsException, MBeanRegistrationException,
317 NotCompliantMBeanException {
318
319 // ------------------------------
320 // ------------------------------
321 Class theClass = object.getClass();
322
323 Introspector.checkCompliance(theClass);
324
325 final String infoClassName = getNewMBeanClassName(object);
326
327 checkMBeanPermission(infoClassName, null, name, "registerMBean");
328 checkMBeanTrustPermission(theClass);
329
330 return registerObject(infoClassName, object, name);
331 }
332
333 private static String getNewMBeanClassName(Object mbeanToRegister)
334 throws NotCompliantMBeanException {
335 if (mbeanToRegister instanceof DynamicMBean) {
336 DynamicMBean mbean = (DynamicMBean) mbeanToRegister;
337 final String name;
338 try {
339 name = mbean.getMBeanInfo().getClassName();
340 } catch (Exception e) {
341 // Includes case where getMBeanInfo() returns null
342 NotCompliantMBeanException ncmbe =
343 new NotCompliantMBeanException("Bad getMBeanInfo()");
344 ncmbe.initCause(e);
345 throw ncmbe;
346 }
347 if (name == null) {
348 final String msg = "MBeanInfo has null class name";
349 throw new NotCompliantMBeanException(msg);
350 }
351 return name;
352 } else
353 return mbeanToRegister.getClass().getName();
354 }
355
356 private final Set<ObjectName> beingUnregistered =
357 new HashSet<ObjectName>();
358
359 public void unregisterMBean(ObjectName name)
360 throws InstanceNotFoundException, MBeanRegistrationException {
361
362 if (name == null) {
363 final RuntimeException wrapped =
364 new IllegalArgumentException("Object name cannot be null");
365 throw new RuntimeOperationsException(wrapped,
366 "Exception occurred trying to unregister the MBean");
367 }
368
369 name = nonDefaultDomain(name);
370
371 /* The semantics of preDeregister are tricky. If it throws an
372 exception, then the unregisterMBean fails. This allows an
373 MBean to refuse to be unregistered. If it returns
374 successfully, then the unregisterMBean can proceed. In
375 this case the preDeregister may have cleaned up some state,
376 and will not expect to be called a second time. So if two
377 threads try to unregister the same MBean at the same time
378 then one of them must wait for the other one to either (a)
379 call preDeregister and get an exception or (b) call
380 preDeregister successfully and unregister the MBean.
381 Suppose thread T1 is unregistering an MBean and thread T2
382 is trying to unregister the same MBean, so waiting for T1.
383 Then a deadlock is possible if the preDeregister for T1
384 ends up needing a lock held by T2. Given the semantics
385 just described, there does not seem to be any way to avoid
386 this. This will not happen to code where it is clear for
387 any given MBean what thread may unregister that MBean.
388
389 On the other hand we clearly do not want a thread that is
390 unregistering MBean A to have to wait for another thread
391 that is unregistering another MBean B (see bug 6318664). A
392 deadlock in this situation could reasonably be considered
393 gratuitous. So holding a global lock across the
394 preDeregister call would be bad.
395
396 So we have a set of ObjectNames that some thread is
397 currently unregistering. When a thread wants to unregister
398 a name, it must first check if the name is in the set, and
399 if so it must wait. When a thread successfully unregisters
400 a name it removes the name from the set and notifies any
401 waiting threads that the set has changed.
402
403 This implies that we must be very careful to ensure that
404 the name is removed from the set and waiters notified, no
405 matter what code path is taken. */
406
407 synchronized (beingUnregistered) {
408 while (beingUnregistered.contains(name)) {
409 try {
410 beingUnregistered.wait();
411 } catch (InterruptedException e) {
412 throw new MBeanRegistrationException(e, e.toString());
413 // pretend the exception came from preDeregister;
414 // in another execution sequence it could have
415 }
416 }
417 beingUnregistered.add(name);
418 }
419
420 try {
421 exclusiveUnregisterMBean(name);
422 } finally {
423 synchronized (beingUnregistered) {
424 beingUnregistered.remove(name);
425 beingUnregistered.notifyAll();
426 }
427 }
428 }
429
430 private void exclusiveUnregisterMBean(ObjectName name)
431 throws InstanceNotFoundException, MBeanRegistrationException {
432
433 DynamicMBean instance = getMBean(name);
434 // may throw InstanceNotFoundException
435
436 checkMBeanPermission(instance, null, name, "unregisterMBean");
437
438 if (instance instanceof MBeanRegistration)
439 preDeregisterInvoke((MBeanRegistration) instance);
440
441 repository.remove(name);
442 // may throw InstanceNotFoundException
443
444 /**
445 * Checks if the unregistered MBean is a ClassLoader
446 * If so, it removes the MBean from the default loader repository.
447 */
448
449 Object resource = getResource(instance);
450 if (resource instanceof ClassLoader
451 && resource != server.getClass().getClassLoader()) {
452 final ModifiableClassLoaderRepository clr =
453 instantiator.getClassLoaderRepository();
454 if (clr != null) clr.removeClassLoader(name);
455 }
456
457 // ---------------------
458 // Send deletion event
459 // ---------------------
460 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
461 MBEANSERVER_LOGGER.logp(Level.FINER,
462 DefaultMBeanServerInterceptor.class.getName(),
463 "unregisterMBean", "Send delete notification of object " +
464 name.getCanonicalName());
465 }
466 sendNotification(MBeanServerNotification.UNREGISTRATION_NOTIFICATION,
467 name);
468
469 if (instance instanceof MBeanRegistration)
470 postDeregisterInvoke((MBeanRegistration) instance);
471 }
472
473 public ObjectInstance getObjectInstance(ObjectName name)
474 throws InstanceNotFoundException {
475
476 name = nonDefaultDomain(name);
477 DynamicMBean instance = getMBean(name);
478
479 checkMBeanPermission(instance, null, name, "getObjectInstance");
480
481 final String className = getClassName(instance);
482
483 return new ObjectInstance(name, className);
484 }
485
486 public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
487 SecurityManager sm = System.getSecurityManager();
488 if (sm != null) {
489 // Check if the caller has the right to invoke 'queryMBeans'
490 //
491 checkMBeanPermission((String) null, null, null, "queryMBeans");
492
493 // Perform query without "query".
494 //
495 Set<ObjectInstance> list = queryMBeansImpl(name, null);
496
497 // Check if the caller has the right to invoke 'queryMBeans'
498 // on each specific classname/objectname in the list.
499 //
500 Set<ObjectInstance> allowedList =
501 new HashSet<ObjectInstance>(list.size());
502 for (ObjectInstance oi : list) {
503 try {
504 checkMBeanPermission(oi.getClassName(), null,
505 oi.getObjectName(), "queryMBeans");
506 allowedList.add(oi);
507 } catch (SecurityException e) {
508 // OK: Do not add this ObjectInstance to the list
509 }
510 }
511
512 // Apply query to allowed MBeans only.
513 //
514 return filterListOfObjectInstances(allowedList, query);
515 } else {
516 // Perform query.
517 //
518 return queryMBeansImpl(name, query);
519 }
520 }
521
522 private Set<ObjectInstance> queryMBeansImpl(ObjectName name,
523 QueryExp query) {
524 // Query the MBeans on the repository
525 //
526 Set<NamedObject> list = null;
527 list = repository.query(name, query);
528
529 if (queryByRepo) {
530 // The repository performs the filtering
531 query = null;
532 }
533
534 return (objectInstancesFromFilteredNamedObjects(list, query));
535 }
536
537 public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
538 Set<ObjectName> queryList;
539 SecurityManager sm = System.getSecurityManager();
540 if (sm != null) {
541 // Check if the caller has the right to invoke 'queryNames'
542 //
543 checkMBeanPermission((String) null, null, null, "queryNames");
544
545 // Perform query without "query".
546 //
547 Set<ObjectInstance> list = queryMBeansImpl(name, null);
548
549 // Check if the caller has the right to invoke 'queryNames'
550 // on each specific classname/objectname in the list.
551 //
552 Set<ObjectInstance> allowedList =
553 new HashSet<ObjectInstance>(list.size());
554 for (ObjectInstance oi : list) {
555 try {
556 checkMBeanPermission(oi.getClassName(), null,
557 oi.getObjectName(), "queryNames");
558 allowedList.add(oi);
559 } catch (SecurityException e) {
560 // OK: Do not add this ObjectInstance to the list
561 }
562 }
563
564 // Apply query to allowed MBeans only.
565 //
566 Set<ObjectInstance> queryObjectInstanceList =
567 filterListOfObjectInstances(allowedList, query);
568 queryList = new HashSet<ObjectName>(queryObjectInstanceList.size());
569 for (ObjectInstance oi : queryObjectInstanceList) {
570 queryList.add(oi.getObjectName());
571 }
572 } else {
573 // Perform query.
574 //
575 queryList = queryNamesImpl(name, query);
576 }
577 return queryList;
578 }
579
580 private Set<ObjectName> queryNamesImpl(ObjectName name, QueryExp query) {
581 // Query the MBeans on the repository
582 //
583 Set<NamedObject> list = null;
584 list = repository.query(name, query);
585
586 if (queryByRepo) {
587 // The repository performs the filtering
588 query = null;
589 }
590
591 return (objectNamesFromFilteredNamedObjects(list, query));
592 }
593
594 public boolean isRegistered(ObjectName name) {
595 if (name == null) {
596 throw new RuntimeOperationsException(
597 new IllegalArgumentException("Object name cannot be null"),
598 "Object name cannot be null");
599 }
600
601 name = nonDefaultDomain(name);
602
603// /* Permission check */
604// checkMBeanPermission(null, null, name, "isRegistered");
605
606 return (repository.contains(name));
607 }
608
609 public String[] getDomains() {
610 SecurityManager sm = System.getSecurityManager();
611 if (sm != null) {
612 // Check if the caller has the right to invoke 'getDomains'
613 //
614 checkMBeanPermission((String) null, null, null, "getDomains");
615
616 // Return domains
617 //
618 String[] domains = repository.getDomains();
619
620 // Check if the caller has the right to invoke 'getDomains'
621 // on each specific domain in the list.
622 //
623 List<String> result = new ArrayList<String>(domains.length);
624 for (int i = 0; i < domains.length; i++) {
625 try {
626 ObjectName domain = new ObjectName(domains[i] + ":x=x");
627 checkMBeanPermission((String) null, null, domain, "getDomains");
628 result.add(domains[i]);
629 } catch (MalformedObjectNameException e) {
630 // Should never occur... But let's log it just in case.
631 if (MBEANSERVER_LOGGER.isLoggable(Level.SEVERE)) {
632 MBEANSERVER_LOGGER.logp(Level.SEVERE,
633 DefaultMBeanServerInterceptor.class.getName(),
634 "getDomains",
635 "Failed to check permission for domain = " +
636 domains[i], e);
637 }
638 } catch (SecurityException e) {
639 // OK: Do not add this domain to the list
640 }
641 }
642
643 // Make an array from result.
644 //
645 return result.toArray(new String[result.size()]);
646 } else {
647 return repository.getDomains();
648 }
649 }
650
651 public Integer getMBeanCount() {
652 return (repository.getCount());
653 }
654
655 public Object getAttribute(ObjectName name, String attribute)
656 throws MBeanException, AttributeNotFoundException,
657 InstanceNotFoundException, ReflectionException {
658
659 if (name == null) {
660 throw new RuntimeOperationsException(new
661 IllegalArgumentException("Object name cannot be null"),
662 "Exception occurred trying to invoke the getter on the MBean");
663 }
664 if (attribute == null) {
665 throw new RuntimeOperationsException(new
666 IllegalArgumentException("Attribute cannot be null"),
667 "Exception occurred trying to invoke the getter on the MBean");
668 }
669
670 name = nonDefaultDomain(name);
671
672 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
673 MBEANSERVER_LOGGER.logp(Level.FINER,
674 DefaultMBeanServerInterceptor.class.getName(),
675 "getAttribute",
676 "Attribute = " + attribute + ", ObjectName = " + name);
677 }
678
679 final DynamicMBean instance = getMBean(name);
680 checkMBeanPermission(instance, attribute, name, "getAttribute");
681
682 try {
683 return instance.getAttribute(attribute);
684 } catch (AttributeNotFoundException e) {
685 throw e;
686 } catch (Throwable t) {
687 rethrowMaybeMBeanException(t);
688 throw new AssertionError(); // not reached
689 }
690 }
691
692 public AttributeList getAttributes(ObjectName name, String[] attributes)
693 throws InstanceNotFoundException, ReflectionException {
694
695 if (name == null) {
696 throw new RuntimeOperationsException(new
697 IllegalArgumentException("ObjectName name cannot be null"),
698 "Exception occurred trying to invoke the getter on the MBean");
699 }
700
701 if (attributes == null) {
702 throw new RuntimeOperationsException(new
703 IllegalArgumentException("Attributes cannot be null"),
704 "Exception occurred trying to invoke the getter on the MBean");
705 }
706
707 name = nonDefaultDomain(name);
708
709 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
710 MBEANSERVER_LOGGER.logp(Level.FINER,
711 DefaultMBeanServerInterceptor.class.getName(),
712 "getAttributes", "ObjectName = " + name);
713 }
714
715 final DynamicMBean instance = getMBean(name);
716 final String[] allowedAttributes;
717 final SecurityManager sm = System.getSecurityManager();
718 if (sm == null)
719 allowedAttributes = attributes;
720 else {
721 final String classname = getClassName(instance);
722
723 // Check if the caller has the right to invoke 'getAttribute'
724 //
725 checkMBeanPermission(classname, null, name, "getAttribute");
726
727 // Check if the caller has the right to invoke 'getAttribute'
728 // on each specific attribute
729 //
730 List<String> allowedList =
731 new ArrayList<String>(attributes.length);
732 for (String attr : attributes) {
733 try {
734 checkMBeanPermission(classname, attr,
735 name, "getAttribute");
736 allowedList.add(attr);
737 } catch (SecurityException e) {
738 // OK: Do not add this attribute to the list
739 }
740 }
741 allowedAttributes = allowedList.toArray(new String[0]);
742 }
743
744 try {
745 return instance.getAttributes(allowedAttributes);
746 } catch (Throwable t) {
747 rethrow(t);
748 throw new AssertionError();
749 }
750 }
751
752 public void setAttribute(ObjectName name, Attribute attribute)
753 throws InstanceNotFoundException, AttributeNotFoundException,
754 InvalidAttributeValueException, MBeanException,
755 ReflectionException {
756
757 if (name == null) {
758 throw new RuntimeOperationsException(new
759 IllegalArgumentException("ObjectName name cannot be null"),
760 "Exception occurred trying to invoke the setter on the MBean");
761 }
762
763 if (attribute == null) {
764 throw new RuntimeOperationsException(new
765 IllegalArgumentException("Attribute cannot be null"),
766 "Exception occurred trying to invoke the setter on the MBean");
767 }
768
769 name = nonDefaultDomain(name);
770
771 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
772 MBEANSERVER_LOGGER.logp(Level.FINER,
773 DefaultMBeanServerInterceptor.class.getName(),
774 "setAttribute", "ObjectName = " + name +
775 ", Attribute = " + attribute.getName());
776 }
777
778 DynamicMBean instance = getMBean(name);
779 checkMBeanPermission(instance, attribute.getName(),
780 name, "setAttribute");
781
782 try {
783 instance.setAttribute(attribute);
784 } catch (AttributeNotFoundException e) {
785 throw e;
786 } catch (InvalidAttributeValueException e) {
787 throw e;
788 } catch (Throwable t) {
789 rethrowMaybeMBeanException(t);
790 throw new AssertionError();
791 }
792 }
793
794 public AttributeList setAttributes(ObjectName name,
795 AttributeList attributes)
796 throws InstanceNotFoundException, ReflectionException {
797
798 if (name == null) {
799 throw new RuntimeOperationsException(new
800 IllegalArgumentException("ObjectName name cannot be null"),
801 "Exception occurred trying to invoke the setter on the MBean");
802 }
803
804 if (attributes == null) {
805 throw new RuntimeOperationsException(new
806 IllegalArgumentException("AttributeList cannot be null"),
807 "Exception occurred trying to invoke the setter on the MBean");
808 }
809
810 name = nonDefaultDomain(name);
811
812 final DynamicMBean instance = getMBean(name);
813 final AttributeList allowedAttributes;
814 final SecurityManager sm = System.getSecurityManager();
815 if (sm == null)
816 allowedAttributes = attributes;
817 else {
818 String classname = getClassName(instance);
819
820 // Check if the caller has the right to invoke 'setAttribute'
821 //
822 checkMBeanPermission(classname, null, name, "setAttribute");
823
824 // Check if the caller has the right to invoke 'setAttribute'
825 // on each specific attribute
826 //
827 allowedAttributes = new AttributeList(attributes.size());
828 for (Iterator i = attributes.iterator(); i.hasNext();) {
829 try {
830 Attribute attribute = (Attribute) i.next();
831 checkMBeanPermission(classname, attribute.getName(),
832 name, "setAttribute");
833 allowedAttributes.add(attribute);
834 } catch (SecurityException e) {
835 // OK: Do not add this attribute to the list
836 }
837 }
838 }
839 try {
840 return instance.setAttributes(allowedAttributes);
841 } catch (Throwable t) {
842 rethrow(t);
843 throw new AssertionError();
844 }
845 }
846
847 public Object invoke(ObjectName name, String operationName,
848 Object params[], String signature[])
849 throws InstanceNotFoundException, MBeanException,
850 ReflectionException {
851
852 name = nonDefaultDomain(name);
853
854 DynamicMBean instance = getMBean(name);
855 checkMBeanPermission(instance, operationName, name, "invoke");
856 try {
857 return instance.invoke(operationName, params, signature);
858 } catch (Throwable t) {
859 rethrowMaybeMBeanException(t);
860 throw new AssertionError();
861 }
862 }
863
864 /* Centralize some of the tedious exception wrapping demanded by the JMX
865 spec. */
866 private static void rethrow(Throwable t)
867 throws ReflectionException {
868 try {
869 throw t;
870 } catch (ReflectionException e) {
871 throw e;
872 } catch (RuntimeOperationsException e) {
873 throw e;
874 } catch (RuntimeErrorException e) {
875 throw e;
876 } catch (RuntimeException e) {
877 throw new RuntimeMBeanException(e, e.toString());
878 } catch (Error e) {
879 throw new RuntimeErrorException(e, e.toString());
880 } catch (Throwable t2) {
881 // should not happen
882 throw new RuntimeException("Unexpected exception", t2);
883 }
884 }
885
886 private static void rethrowMaybeMBeanException(Throwable t)
887 throws ReflectionException, MBeanException {
888 if (t instanceof MBeanException)
889 throw (MBeanException) t;
890 rethrow(t);
891 }
892
893 /**
894 * Register <code>object</code> in the repository, with the
895 * given <code>name</code>.
896 * This method is called by the various createMBean() flavours
897 * and by registerMBean() after all MBean compliance tests
898 * have been performed.
899 * <p>
900 * This method does not performed any kind of test compliance,
901 * and the caller should make sure that the given <code>object</code>
902 * is MBean compliant.
903 * <p>
904 * This methods performed all the basic steps needed for object
905 * registration:
906 * <ul>
907 * <li>If the <code>object</code> implements the MBeanRegistration
908 * interface, it invokes preRegister() on the object.</li>
909 * <li>Then the object is added to the repository with the given
910 * <code>name</code>.</li>
911 * <li>Finally, if the <code>object</code> implements the
912 * MBeanRegistration interface, it invokes postRegister()
913 * on the object.</li>
914 * </ul>
915 * @param object A reference to a MBean compliant object.
916 * @param name The ObjectName of the <code>object</code> MBean.
917 * @return the actual ObjectName with which the object was registered.
918 * @exception InstanceAlreadyExistsException if an object is already
919 * registered with that name.
920 * @exception MBeanRegistrationException if an exception occurs during
921 * registration.
922 **/
923 private ObjectInstance registerObject(String classname,
924 Object object, ObjectName name)
925 throws InstanceAlreadyExistsException,
926 MBeanRegistrationException,
927 NotCompliantMBeanException {
928
929 if (object == null) {
930 final RuntimeException wrapped =
931 new IllegalArgumentException("Cannot add null object");
932 throw new RuntimeOperationsException(wrapped,
933 "Exception occurred trying to register the MBean");
934 }
935
936 DynamicMBean mbean = Introspector.makeDynamicMBean(object);
937
938 return registerDynamicMBean(classname, mbean, name);
939 }
940
941 private ObjectInstance registerDynamicMBean(String classname,
942 DynamicMBean mbean,
943 ObjectName name)
944 throws InstanceAlreadyExistsException,
945 MBeanRegistrationException,
946 NotCompliantMBeanException {
947
948
949 name = nonDefaultDomain(name);
950
951 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
952 MBEANSERVER_LOGGER.logp(Level.FINER,
953 DefaultMBeanServerInterceptor.class.getName(),
954 "registerMBean", "ObjectName = " + name);
955 }
956
957 ObjectName logicalName = name;
958
959 if (mbean instanceof MBeanRegistration) {
960 MBeanRegistration reg = (MBeanRegistration) mbean;
961 logicalName = preRegisterInvoke(reg, name, server);
962 if (mbean instanceof DynamicMBean2) {
963 try {
964 ((DynamicMBean2) mbean).preRegister2(server, logicalName);
965 } catch (Exception e) {
966 postRegisterInvoke(reg, false, false);
967 if (e instanceof RuntimeException)
968 throw (RuntimeException) e;
969 if (e instanceof InstanceAlreadyExistsException)
970 throw (InstanceAlreadyExistsException) e;
971 throw new RuntimeException(e);
972 }
973 }
974
975 if (logicalName != name && logicalName != null) {
976 logicalName =
977 ObjectName.getInstance(nonDefaultDomain(logicalName));
978 }
979 }
980
981 checkMBeanPermission(classname, null, logicalName, "registerMBean");
982
983 final ObjectInstance result;
984 if (logicalName!=null) {
985 result = new ObjectInstance(logicalName, classname);
986 internal_addObject(mbean, logicalName);
987 } else {
988 if (mbean instanceof MBeanRegistration)
989 postRegisterInvoke((MBeanRegistration) mbean, false, true);
990 final RuntimeException wrapped =
991 new IllegalArgumentException("No object name specified");
992 throw new RuntimeOperationsException(wrapped,
993 "Exception occurred trying to register the MBean");
994 }
995
996 if (mbean instanceof MBeanRegistration)
997 postRegisterInvoke((MBeanRegistration) mbean, true, false);
998
999 /**
1000 * Checks if the newly registered MBean is a ClassLoader
1001 * If so, tell the ClassLoaderRepository (CLR) about it. We do
1002 * this even if the object is a PrivateClassLoader. In that
1003 * case, the CLR remembers the loader for use when it is
1004 * explicitly named (e.g. as the loader in createMBean) but
1005 * does not add it to the list that is consulted by
1006 * ClassLoaderRepository.loadClass.
1007 */
1008 final Object resource = getResource(mbean);
1009 if (resource instanceof ClassLoader) {
1010 final ModifiableClassLoaderRepository clr =
1011 instantiator.getClassLoaderRepository();
1012 if (clr == null) {
1013 final RuntimeException wrapped =
1014 new IllegalArgumentException(
1015 "Dynamic addition of class loaders is not supported");
1016 throw new RuntimeOperationsException(wrapped,
1017 "Exception occurred trying to register the MBean as a class loader");
1018 }
1019 clr.addClassLoader(logicalName, (ClassLoader) resource);
1020 }
1021
1022 return result;
1023 }
1024
1025 private static ObjectName preRegisterInvoke(MBeanRegistration moi,
1026 ObjectName name,
1027 MBeanServer mbs)
1028 throws InstanceAlreadyExistsException, MBeanRegistrationException {
1029
1030 final ObjectName newName;
1031
1032 try {
1033 newName = moi.preRegister(mbs, name);
1034 } catch (RuntimeException e) {
1035 throw new RuntimeMBeanException(e,
1036 "RuntimeException thrown in preRegister method");
1037 } catch (Error er) {
1038 throw new RuntimeErrorException(er,
1039 "Error thrown in preRegister method");
1040 } catch (MBeanRegistrationException r) {
1041 throw r;
1042 } catch (Exception ex) {
1043 throw new MBeanRegistrationException(ex,
1044 "Exception thrown in preRegister method");
1045 }
1046
1047 if (newName != null) return newName;
1048 else return name;
1049 }
1050
1051 private static void postRegisterInvoke(MBeanRegistration moi,
1052 boolean registrationDone,
1053 boolean registerFailed) {
1054
1055 if (registerFailed && moi instanceof DynamicMBean2)
1056 ((DynamicMBean2) moi).registerFailed();
1057 try {
1058 moi.postRegister(new Boolean(registrationDone));
1059 } catch (RuntimeException e) {
1060 throw new RuntimeMBeanException(e,
1061 "RuntimeException thrown in postRegister method");
1062 } catch (Error er) {
1063 throw new RuntimeErrorException(er,
1064 "Error thrown in postRegister method");
1065 }
1066 }
1067
1068 private static void preDeregisterInvoke(MBeanRegistration moi)
1069 throws MBeanRegistrationException {
1070 try {
1071 moi.preDeregister();
1072 } catch (RuntimeException e) {
1073 throw new RuntimeMBeanException(e,
1074 "RuntimeException thrown in preDeregister method");
1075 } catch (Error er) {
1076 throw new RuntimeErrorException(er,
1077 "Error thrown in preDeregister method");
1078 } catch (MBeanRegistrationException t) {
1079 throw t;
1080 } catch (Exception ex) {
1081 throw new MBeanRegistrationException(ex,
1082 "Exception thrown in preDeregister method");
1083 }
1084 }
1085
1086 private static void postDeregisterInvoke(MBeanRegistration moi) {
1087 try {
1088 moi.postDeregister();
1089 } catch (RuntimeException e) {
1090 throw new RuntimeMBeanException(e,
1091 "RuntimeException thrown in postDeregister method");
1092 } catch (Error er) {
1093 throw new RuntimeErrorException(er,
1094 "Error thrown in postDeregister method");
1095 }
1096 }
1097
1098 /**
1099 * Gets a specific MBean controlled by the DefaultMBeanServerInterceptor.
1100 * The name must have a non-default domain.
1101 */
1102 private DynamicMBean getMBean(ObjectName name)
1103 throws InstanceNotFoundException {
1104
1105 if (name == null) {
1106 throw new RuntimeOperationsException(new
1107 IllegalArgumentException("Object name cannot be null"),
1108 "Exception occurred trying to get an MBean");
1109 }
1110 DynamicMBean obj = null;
1111 obj = repository.retrieve(name);
1112 if (obj == null) {
1113 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
1114 MBEANSERVER_LOGGER.logp(Level.FINER,
1115 DefaultMBeanServerInterceptor.class.getName(),
1116 "getMBean", name + " : Found no object");
1117 }
1118 throw new InstanceNotFoundException(name.toString());
1119 }
1120 return obj;
1121 }
1122
1123 private static Object getResource(DynamicMBean mbean) {
1124 if (mbean instanceof DynamicMBean2)
1125 return ((DynamicMBean2) mbean).getResource();
1126 else
1127 return mbean;
1128 }
1129
1130 private ObjectName nonDefaultDomain(ObjectName name) {
1131 if (name == null || name.getDomain().length() > 0)
1132 return name;
1133
1134 /* The ObjectName looks like ":a=b", and that's what its
1135 toString() will return in this implementation. So
1136 we can just stick the default domain in front of it
1137 to get a non-default-domain name. We depend on the
1138 fact that toString() works like that and that it
1139 leaves wildcards in place (so we can detect an error
1140 if one is supplied where it shouldn't be). */
1141 final String completeName = domain + name;
1142
1143 try {
1144 return new ObjectName(completeName);
1145 } catch (MalformedObjectNameException e) {
1146 final String msg =
1147 "Unexpected default domain problem: " + completeName + ": " +
1148 e;
1149 throw EnvHelp.initCause(new IllegalArgumentException(msg), e);
1150 }
1151 }
1152
1153 public String getDefaultDomain() {
1154 return domain;
1155 }
1156
1157 /*
1158 * Notification handling.
1159 *
1160 * This is not trivial, because the MBeanServer translates the
1161 * source of a received notification from a reference to an MBean
1162 * into the ObjectName of that MBean. While that does make
1163 * notification sending easier for MBean writers, it comes at a
1164 * considerable cost. We need to replace the source of a
1165 * notification, which is basically wrong if there are also
1166 * listeners registered directly with the MBean (without going
1167 * through the MBean server). We also need to wrap the listener
1168 * supplied by the client of the MBeanServer with a listener that
1169 * performs the substitution before forwarding. This is why we
1170 * strongly discourage people from putting MBean references in the
1171 * source of their notifications. Instead they should arrange to
1172 * put the ObjectName there themselves.
1173 *
1174 * However, existing code relies on the substitution, so we are
1175 * stuck with it.
1176 *
1177 * Here's how we handle it. When you add a listener, we make a
1178 * ListenerWrapper around it. We look that up in the
1179 * listenerWrappers map, and if there was already a wrapper for
1180 * that listener with the given ObjectName, we reuse it. This map
1181 * is a WeakHashMap, so a listener that is no longer registered
1182 * with any MBean can be garbage collected.
1183 *
1184 * We cannot use simpler solutions such as always creating a new
1185 * wrapper or always registering the same listener with the MBean
1186 * and using the handback to find the client's original listener.
1187 * The reason is that we need to support the removeListener
1188 * variant that removes all (listener,filter,handback) triples on
1189 * a broadcaster that have a given listener. And we do not have
1190 * any way to inspect a broadcaster's internal list of triples.
1191 * So the same client listener must always map to the same
1192 * listener registered with the broadcaster.
1193 *
1194 * Another possible solution would be to map from ObjectName to
1195 * list of listener wrappers (or IdentityHashMap of listener
1196 * wrappers), making this list the first time a listener is added
1197 * on a given MBean, and removing it when the MBean is removed.
1198 * This is probably more costly in memory, but could be useful if
1199 * some day we don't want to rely on weak references.
1200 */
1201 public void addNotificationListener(ObjectName name,
1202 NotificationListener listener,
1203 NotificationFilter filter,
1204 Object handback)
1205 throws InstanceNotFoundException {
1206
1207 // ------------------------------
1208 // ------------------------------
1209 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
1210 MBEANSERVER_LOGGER.logp(Level.FINER,
1211 DefaultMBeanServerInterceptor.class.getName(),
1212 "addNotificationListener", "ObjectName = " + name);
1213 }
1214
1215 DynamicMBean instance = getMBean(name);
1216 checkMBeanPermission(instance, null, name, "addNotificationListener");
1217
1218 NotificationBroadcaster broadcaster =
1219 getNotificationBroadcaster(name, instance,
1220 NotificationBroadcaster.class);
1221
1222 // ------------------
1223 // Check listener
1224 // ------------------
1225 if (listener == null) {
1226 throw new RuntimeOperationsException(new
1227 IllegalArgumentException("Null listener"),"Null listener");
1228 }
1229
1230 NotificationListener listenerWrapper =
1231 getListenerWrapper(listener, name, broadcaster, true);
1232 broadcaster.addNotificationListener(listenerWrapper, filter, handback);
1233 }
1234
1235 public void addNotificationListener(ObjectName name,
1236 ObjectName listener,
1237 NotificationFilter filter,
1238 Object handback)
1239 throws InstanceNotFoundException {
1240
1241 // ------------------------------
1242 // ------------------------------
1243
1244 // ----------------
1245 // Get listener object
1246 // ----------------
1247 DynamicMBean instance = getMBean(listener);
1248 Object resource = getResource(instance);
1249 if (!(resource instanceof NotificationListener)) {
1250 throw new RuntimeOperationsException(new
1251 IllegalArgumentException(listener.getCanonicalName()),
1252 "The MBean " + listener.getCanonicalName() +
1253 "does not implement the NotificationListener interface") ;
1254 }
1255
1256 // ----------------
1257 // Add a listener on an MBean
1258 // ----------------
1259 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
1260 MBEANSERVER_LOGGER.logp(Level.FINER,
1261 DefaultMBeanServerInterceptor.class.getName(),
1262 "addNotificationListener",
1263 "ObjectName = " + name + ", Listener = " + listener);
1264 }
1265 server.addNotificationListener(name,(NotificationListener) resource,
1266 filter, handback) ;
1267 }
1268
1269 public void removeNotificationListener(ObjectName name,
1270 NotificationListener listener)
1271 throws InstanceNotFoundException, ListenerNotFoundException {
1272 removeNotificationListener(name, listener, null, null, true);
1273 }
1274
1275 public void removeNotificationListener(ObjectName name,
1276 NotificationListener listener,
1277 NotificationFilter filter,
1278 Object handback)
1279 throws InstanceNotFoundException, ListenerNotFoundException {
1280 removeNotificationListener(name, listener, filter, handback, false);
1281 }
1282
1283 public void removeNotificationListener(ObjectName name,
1284 ObjectName listener)
1285 throws InstanceNotFoundException, ListenerNotFoundException {
1286 NotificationListener instance = getListener(listener);
1287
1288 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
1289 MBEANSERVER_LOGGER.logp(Level.FINER,
1290 DefaultMBeanServerInterceptor.class.getName(),
1291 "removeNotificationListener",
1292 "ObjectName = " + name + ", Listener = " + listener);
1293 }
1294 server.removeNotificationListener(name, instance);
1295 }
1296
1297 public void removeNotificationListener(ObjectName name,
1298 ObjectName listener,
1299 NotificationFilter filter,
1300 Object handback)
1301 throws InstanceNotFoundException, ListenerNotFoundException {
1302
1303 NotificationListener instance = getListener(listener);
1304
1305 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
1306 MBEANSERVER_LOGGER.logp(Level.FINER,
1307 DefaultMBeanServerInterceptor.class.getName(),
1308 "removeNotificationListener",
1309 "ObjectName = " + name + ", Listener = " + listener);
1310 }
1311 server.removeNotificationListener(name, instance, filter, handback);
1312 }
1313
1314 private NotificationListener getListener(ObjectName listener)
1315 throws ListenerNotFoundException {
1316 // ----------------
1317 // Get listener object
1318 // ----------------
1319 DynamicMBean instance;
1320 try {
1321 instance = getMBean(listener);
1322 } catch (InstanceNotFoundException e) {
1323 throw EnvHelp.initCause(
1324 new ListenerNotFoundException(e.getMessage()), e);
1325 }
1326
1327 Object resource = getResource(instance);
1328 if (!(resource instanceof NotificationListener)) {
1329 final RuntimeException exc =
1330 new IllegalArgumentException(listener.getCanonicalName());
1331 final String msg =
1332 "MBean " + listener.getCanonicalName() + " does not " +
1333 "implement " + NotificationListener.class.getName();
1334 throw new RuntimeOperationsException(exc, msg);
1335 }
1336 return (NotificationListener) resource;
1337 }
1338
1339 private void removeNotificationListener(ObjectName name,
1340 NotificationListener listener,
1341 NotificationFilter filter,
1342 Object handback,
1343 boolean removeAll)
1344 throws InstanceNotFoundException, ListenerNotFoundException {
1345
1346 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
1347 MBEANSERVER_LOGGER.logp(Level.FINER,
1348 DefaultMBeanServerInterceptor.class.getName(),
1349 "removeNotificationListener", "ObjectName = " + name);
1350 }
1351
1352 DynamicMBean instance = getMBean(name);
1353 checkMBeanPermission(instance, null, name,
1354 "removeNotificationListener");
1355 Object resource = getResource(instance);
1356
1357 /* We could simplify the code by assigning broadcaster after
1358 assigning listenerWrapper, but that would change the error
1359 behavior when both the broadcaster and the listener are
1360 erroneous. */
1361
1362 Class<? extends NotificationBroadcaster> reqClass =
1363 removeAll ? NotificationBroadcaster.class : NotificationEmitter.class;
1364 NotificationBroadcaster broadcaster =
1365 getNotificationBroadcaster(name, instance, reqClass);
1366
1367 NotificationListener listenerWrapper =
1368 getListenerWrapper(listener, name, resource, false);
1369
1370 if (listenerWrapper == null)
1371 throw new ListenerNotFoundException("Unknown listener");
1372
1373 if (removeAll)
1374 broadcaster.removeNotificationListener(listenerWrapper);
1375 else {
1376 NotificationEmitter emitter = (NotificationEmitter) broadcaster;
1377 emitter.removeNotificationListener(listenerWrapper,
1378 filter,
1379 handback);
1380 }
1381 }
1382
1383 private static <T extends NotificationBroadcaster>
1384 T getNotificationBroadcaster(ObjectName name, Object instance,
1385 Class<T> reqClass) {
1386 if (instance instanceof DynamicMBean2)
1387 instance = ((DynamicMBean2) instance).getResource();
1388 if (reqClass.isInstance(instance))
1389 return reqClass.cast(instance);
1390 final RuntimeException exc =
1391 new IllegalArgumentException(name.getCanonicalName());
1392 final String msg =
1393 "MBean " + name.getCanonicalName() + " does not " +
1394 "implement " + reqClass.getName();
1395 throw new RuntimeOperationsException(exc, msg);
1396 }
1397
1398 public MBeanInfo getMBeanInfo(ObjectName name)
1399 throws InstanceNotFoundException, IntrospectionException,
1400 ReflectionException {
1401
1402 // ------------------------------
1403 // ------------------------------
1404
1405 DynamicMBean moi = getMBean(name);
1406 final MBeanInfo mbi;
1407 try {
1408 mbi = moi.getMBeanInfo();
1409 } catch (RuntimeMBeanException e) {
1410 throw e;
1411 } catch (RuntimeErrorException e) {
1412 throw e;
1413 } catch (RuntimeException e) {
1414 throw new RuntimeMBeanException(e,
1415 "getMBeanInfo threw RuntimeException");
1416 } catch (Error e) {
1417 throw new RuntimeErrorException(e, "getMBeanInfo threw Error");
1418 }
1419 if (mbi == null)
1420 throw new JMRuntimeException("MBean " + name +
1421 "has no MBeanInfo");
1422
1423 checkMBeanPermission(mbi.getClassName(), null, name, "getMBeanInfo");
1424
1425 return mbi;
1426 }
1427
1428 public boolean isInstanceOf(ObjectName name, String className)
1429 throws InstanceNotFoundException {
1430
1431 DynamicMBean instance = getMBean(name);
1432 checkMBeanPermission(instance, null, name, "isInstanceOf");
1433
1434 try {
1435 if (instance instanceof DynamicMBean2) {
1436 Object resource = ((DynamicMBean2) instance).getResource();
1437 ClassLoader loader = resource.getClass().getClassLoader();
1438 Class<?> c = Class.forName(className, false, loader);
1439 return c.isInstance(resource);
1440 }
1441
1442 final String cn = getClassName(instance);
1443 if (cn.equals(className))
1444 return true;
1445 final ClassLoader cl = instance.getClass().getClassLoader();
1446
1447 final Class<?> classNameClass = Class.forName(className, false, cl);
1448 if (classNameClass.isInstance(instance))
1449 return true;
1450
1451 final Class<?> instanceClass = Class.forName(cn, false, cl);
1452 return classNameClass.isAssignableFrom(instanceClass);
1453 } catch (Exception x) {
1454 /* Could be SecurityException or ClassNotFoundException */
1455 if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) {
1456 MBEANSERVER_LOGGER.logp(Level.FINEST,
1457 DefaultMBeanServerInterceptor.class.getName(),
1458 "isInstanceOf", "Exception calling isInstanceOf", x);
1459 }
1460 return false;
1461 }
1462
1463 }
1464
1465 /**
1466 * <p>Return the {@link java.lang.ClassLoader} that was used for
1467 * loading the class of the named MBean.
1468 * @param mbeanName The ObjectName of the MBean.
1469 * @return The ClassLoader used for that MBean.
1470 * @exception InstanceNotFoundException if the named MBean is not found.
1471 */
1472 public ClassLoader getClassLoaderFor(ObjectName mbeanName)
1473 throws InstanceNotFoundException {
1474
1475 DynamicMBean instance = getMBean(mbeanName);
1476 checkMBeanPermission(instance, null, mbeanName, "getClassLoaderFor");
1477 return getResource(instance).getClass().getClassLoader();
1478 }
1479
1480 /**
1481 * <p>Return the named {@link java.lang.ClassLoader}.
1482 * @param loaderName The ObjectName of the ClassLoader.
1483 * @return The named ClassLoader.
1484 * @exception InstanceNotFoundException if the named ClassLoader
1485 * is not found.
1486 */
1487 public ClassLoader getClassLoader(ObjectName loaderName)
1488 throws InstanceNotFoundException {
1489
1490 if (loaderName == null) {
1491 checkMBeanPermission((String) null, null, null, "getClassLoader");
1492 return server.getClass().getClassLoader();
1493 }
1494
1495 DynamicMBean instance = getMBean(loaderName);
1496 checkMBeanPermission(instance, null, loaderName, "getClassLoader");
1497
1498 Object resource = getResource(instance);
1499
1500 /* Check if the given MBean is a ClassLoader */
1501 if (!(resource instanceof ClassLoader))
1502 throw new InstanceNotFoundException(loaderName.toString() +
1503 " is not a classloader");
1504
1505 return (ClassLoader) resource;
1506 }
1507
1508 /**
1509 * Adds a MBean in the repository
1510 */
1511 private void internal_addObject(DynamicMBean object, ObjectName logicalName)
1512 throws InstanceAlreadyExistsException {
1513
1514 // ------------------------------
1515 // ------------------------------
1516
1517 // Let the repository do the work.
1518
1519 try {
1520 repository.addMBean(object, logicalName);
1521 } catch (InstanceAlreadyExistsException e) {
1522 if (object instanceof MBeanRegistration) {
1523 postRegisterInvoke((MBeanRegistration) object, false, true);
1524 }
1525 throw e;
1526 }
1527
1528 // ---------------------
1529 // Send create event
1530 // ---------------------
1531 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
1532 MBEANSERVER_LOGGER.logp(Level.FINER,
1533 DefaultMBeanServerInterceptor.class.getName(),
1534 "addObject", "Send create notification of object " +
1535 logicalName.getCanonicalName());
1536 }
1537
1538 sendNotification(MBeanServerNotification.REGISTRATION_NOTIFICATION,
1539 logicalName ) ;
1540 }
1541
1542 /**
1543 * Sends an MBeanServerNotifications with the specified type for the
1544 * MBean with the specified ObjectName
1545 */
1546 private void sendNotification(String NotifType, ObjectName name) {
1547
1548 // ------------------------------
1549 // ------------------------------
1550
1551 // ---------------------
1552 // Create notification
1553 // ---------------------
1554 MBeanServerNotification notif = new MBeanServerNotification(
1555 NotifType,MBeanServerDelegate.DELEGATE_NAME,0,name);
1556
1557 if (MBEANSERVER_LOGGER.isLoggable(Level.FINER)) {
1558 MBEANSERVER_LOGGER.logp(Level.FINER,
1559 DefaultMBeanServerInterceptor.class.getName(),
1560 "sendNotification", NotifType + " " + name);
1561 }
1562
1563 delegate.sendNotification(notif);
1564 }
1565
1566 /**
1567 * Applies the specified queries to the set of NamedObjects.
1568 */
1569 private Set<ObjectName>
1570 objectNamesFromFilteredNamedObjects(Set<NamedObject> list,
1571 QueryExp query) {
1572 Set<ObjectName> result = new HashSet<ObjectName>();
1573 // No query ...
1574 if (query == null) {
1575 for (NamedObject no : list) {
1576 result.add(no.getName());
1577 }
1578 } else {
1579 // Access the filter
1580 MBeanServer oldServer = QueryEval.getMBeanServer();
1581 query.setMBeanServer(server);
1582 try {
1583 for (NamedObject no : list) {
1584 final DynamicMBean obj = no.getObject();
1585 boolean res;
1586 try {
1587 res = query.apply(no.getName());
1588 } catch (Exception e) {
1589 res = false;
1590 }
1591 if (res) {
1592 result.add(no.getName());
1593 }
1594 }
1595 } finally {
1596 /*
1597 * query.setMBeanServer is probably
1598 * QueryEval.setMBeanServer so put back the old
1599 * value. Since that method uses a ThreadLocal
1600 * variable, this code is only needed for the
1601 * unusual case where the user creates a custom
1602 * QueryExp that calls a nested query on another
1603 * MBeanServer.
1604 */
1605 query.setMBeanServer(oldServer);
1606 }
1607 }
1608 return result;
1609 }
1610
1611 /**
1612 * Applies the specified queries to the set of NamedObjects.
1613 */
1614 private Set<ObjectInstance>
1615 objectInstancesFromFilteredNamedObjects(Set<NamedObject> list,
1616 QueryExp query) {
1617 Set<ObjectInstance> result = new HashSet<ObjectInstance>();
1618 // No query ...
1619 if (query == null) {
1620 for (NamedObject no : list) {
1621 final DynamicMBean obj = no.getObject();
1622 final String className = safeGetClassName(obj);
1623 result.add(new ObjectInstance(no.getName(), className));
1624 }
1625 } else {
1626 // Access the filter
1627 MBeanServer oldServer = QueryEval.getMBeanServer();
1628 query.setMBeanServer(server);
1629 try {
1630 for (NamedObject no : list) {
1631 final DynamicMBean obj = no.getObject();
1632 boolean res;
1633 try {
1634 res = query.apply(no.getName());
1635 } catch (Exception e) {
1636 res = false;
1637 }
1638 if (res) {
1639 String className = safeGetClassName(obj);
1640 result.add(new ObjectInstance(no.getName(), className));
1641 }
1642 }
1643 } finally {
1644 /*
1645 * query.setMBeanServer is probably
1646 * QueryEval.setMBeanServer so put back the old
1647 * value. Since that method uses a ThreadLocal
1648 * variable, this code is only needed for the
1649 * unusual case where the user creates a custom
1650 * QueryExp that calls a nested query on another
1651 * MBeanServer.
1652 */
1653 query.setMBeanServer(oldServer);
1654 }
1655 }
1656 return result;
1657 }
1658
1659 private static String safeGetClassName(DynamicMBean mbean) {
1660 try {
1661 return getClassName(mbean);
1662 } catch (Exception e) {
1663 if (MBEANSERVER_LOGGER.isLoggable(Level.FINEST)) {
1664 MBEANSERVER_LOGGER.logp(Level.FINEST,
1665 DefaultMBeanServerInterceptor.class.getName(),
1666 "safeGetClassName",
1667 "Exception getting MBean class name", e);
1668 }
1669 return null;
1670 }
1671 }
1672
1673 /**
1674 * Applies the specified queries to the set of ObjectInstances.
1675 */
1676 private Set<ObjectInstance>
1677 filterListOfObjectInstances(Set<ObjectInstance> list,
1678 QueryExp query) {
1679 // Null query.
1680 //
1681 if (query == null) {
1682 return list;
1683 } else {
1684 Set<ObjectInstance> result = new HashSet<ObjectInstance>();
1685 // Access the filter.
1686 //
1687 for (ObjectInstance oi : list) {
1688 boolean res = false;
1689 MBeanServer oldServer = QueryEval.getMBeanServer();
1690 query.setMBeanServer(server);
1691 try {
1692 res = query.apply(oi.getObjectName());
1693 } catch (Exception e) {
1694 res = false;
1695 } finally {
1696 /*
1697 * query.setMBeanServer is probably
1698 * QueryEval.setMBeanServer so put back the old
1699 * value. Since that method uses a ThreadLocal
1700 * variable, this code is only needed for the
1701 * unusual case where the user creates a custom
1702 * QueryExp that calls a nested query on another
1703 * MBeanServer.
1704 */
1705 query.setMBeanServer(oldServer);
1706 }
1707 if (res) {
1708 result.add(oi);
1709 }
1710 }
1711 return result;
1712 }
1713 }
1714
1715 /*
1716 * Get the existing wrapper for this listener, name, and mbean, if
1717 * there is one. Otherwise, if "create" is true, create and
1718 * return one. Otherwise, return null.
1719 *
1720 * We use a WeakHashMap so that if the only reference to a user
1721 * listener is in listenerWrappers, it can be garbage collected.
1722 * This requires a certain amount of care, because only the key in
1723 * a WeakHashMap is weak; the value is strong. We need to recover
1724 * the existing wrapper object (not just an object that is equal
1725 * to it), so we would like listenerWrappers to map any
1726 * ListenerWrapper to the canonical ListenerWrapper for that
1727 * (listener,name,mbean) set. But we do not want this canonical
1728 * wrapper to be referenced strongly. Therefore we put it inside
1729 * a WeakReference and that is the value in the WeakHashMap.
1730 */
1731 private NotificationListener getListenerWrapper(NotificationListener l,
1732 ObjectName name,
1733 Object mbean,
1734 boolean create) {
1735 ListenerWrapper wrapper = new ListenerWrapper(l, name, mbean);
1736 synchronized (listenerWrappers) {
1737 WeakReference<ListenerWrapper> ref = listenerWrappers.get(wrapper);
1738 if (ref != null) {
1739 NotificationListener existing = ref.get();
1740 if (existing != null)
1741 return existing;
1742 }
1743 if (create) {
1744 ref = new WeakReference<ListenerWrapper>(wrapper);
1745 listenerWrappers.put(wrapper, ref);
1746 return wrapper;
1747 } else
1748 return null;
1749 }
1750 }
1751
1752 private static class ListenerWrapper implements NotificationListener {
1753 ListenerWrapper(NotificationListener l, ObjectName name,
1754 Object mbean) {
1755 this.listener = l;
1756 this.name = name;
1757 this.mbean = mbean;
1758 }
1759
1760 public void handleNotification(Notification notification,
1761 Object handback) {
1762 if (notification != null) {
1763 if (notification.getSource() == mbean)
1764 notification.setSource(name);
1765 }
1766
1767 /*
1768 * Listeners are not supposed to throw exceptions. If
1769 * this one does, we could remove it from the MBean. It
1770 * might indicate that a connector has stopped working,
1771 * for instance, and there is no point in sending future
1772 * notifications over that connection. However, this
1773 * seems rather drastic, so instead we propagate the
1774 * exception and let the broadcaster handle it.
1775 */
1776 listener.handleNotification(notification, handback);
1777 }
1778
1779 public boolean equals(Object o) {
1780 if (!(o instanceof ListenerWrapper))
1781 return false;
1782 ListenerWrapper w = (ListenerWrapper) o;
1783 return (w.listener == listener && w.mbean == mbean
1784 && w.name.equals(name));
1785 /*
1786 * We compare all three, in case the same MBean object
1787 * gets unregistered and then reregistered under a
1788 * different name, or the same name gets assigned to two
1789 * different MBean objects at different times. We do the
1790 * comparisons in this order to avoid the slow
1791 * ObjectName.equals when possible.
1792 */
1793 }
1794
1795 public int hashCode() {
1796 return (System.identityHashCode(listener) ^
1797 System.identityHashCode(mbean));
1798 /*
1799 * We do not include name.hashCode() in the hash because
1800 * computing it is slow and usually we will not have two
1801 * instances of ListenerWrapper with the same mbean but
1802 * different ObjectNames. That can happen if the MBean is
1803 * unregistered from one name and reregistered with
1804 * another, and there is no garbage collection between; or
1805 * if the same object is registered under two names (which
1806 * is not recommended because MBeanRegistration will
1807 * break). But even in these unusual cases the hash code
1808 * does not have to be unique.
1809 */
1810 }
1811
1812 private NotificationListener listener;
1813 private ObjectName name;
1814 private Object mbean;
1815 }
1816
1817 // SECURITY CHECKS
1818 //----------------
1819
1820 private static String getClassName(DynamicMBean mbean) {
1821 if (mbean instanceof DynamicMBean2)
1822 return ((DynamicMBean2) mbean).getClassName();
1823 else
1824 return mbean.getMBeanInfo().getClassName();
1825 }
1826
1827 private static void checkMBeanPermission(DynamicMBean mbean,
1828 String member,
1829 ObjectName objectName,
1830 String actions) {
1831 SecurityManager sm = System.getSecurityManager();
1832 if (sm != null) {
1833 checkMBeanPermission(safeGetClassName(mbean),
1834 member,
1835 objectName,
1836 actions);
1837 }
1838 }
1839
1840 private static void checkMBeanPermission(String classname,
1841 String member,
1842 ObjectName objectName,
1843 String actions) {
1844 SecurityManager sm = System.getSecurityManager();
1845 if (sm != null) {
1846 Permission perm = new MBeanPermission(classname,
1847 member,
1848 objectName,
1849 actions);
1850 sm.checkPermission(perm);
1851 }
1852 }
1853
1854 private static void checkMBeanTrustPermission(final Class theClass)
1855 throws SecurityException {
1856 SecurityManager sm = System.getSecurityManager();
1857 if (sm != null) {
1858 Permission perm = new MBeanTrustPermission("register");
1859 PrivilegedAction<ProtectionDomain> act =
1860 new PrivilegedAction<ProtectionDomain>() {
1861 public ProtectionDomain run() {
1862 return theClass.getProtectionDomain();
1863 }
1864 };
1865 ProtectionDomain pd = AccessController.doPrivileged(act);
1866 AccessControlContext acc =
1867 new AccessControlContext(new ProtectionDomain[] { pd });
1868 sm.checkPermission(perm, acc);
1869 }
1870 }
1871
1872}