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