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