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