/*
 * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *   Free SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jmx;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;

import java.util.Iterator;
import java.util.Hashtable;
import java.util.Set;

import java.util.logging.Logger;
import java.util.logging.Level;

import java.io.ObjectInputStream;

import javax.management.MBeanServer;
import javax.management.DynamicMBean;
import javax.management.MBeanServerFactory;
import javax.management.MBeanInfo;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.StandardMBean;
import javax.management.ObjectName;
import javax.management.ObjectInstance;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.QueryExp;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import javax.management.IntrospectionException;
import javax.management.MBeanRegistrationException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.NotCompliantMBeanException;
import javax.management.OperationsException;
import javax.management.ListenerNotFoundException;
import javax.management.InvalidAttributeValueException;
import javax.management.AttributeNotFoundException;
import javax.management.RuntimeOperationsException;

import javax.management.loading.ClassLoaderRepository;

import com.caucho.util.L10N;

import com.caucho.log.Log;

import com.caucho.loader.WeakCloseListener;
import com.caucho.loader.Environment;

/**
 * The main interface for retrieving and managing JMX objects.
 */
abstract public class AbstractMBeanServer implements MBeanServer {
  private static final L10N L = new L10N(AbstractMBeanServer.class);
  private static final Logger log = Log.open(AbstractMBeanServer.class);

  static ObjectName SERVER_DELEGATE_NAME;

  // default domain
  private String _defaultDomain;

  /**
   * Creats a new MBeanServer implementation.
   */
  public AbstractMBeanServer(String defaultDomain)
  {
    _defaultDomain = defaultDomain;

    Environment.addClassLoaderListener(new WeakCloseListener(this));
  }

  /**
   * Returns the context implementation.
   */
  protected MBeanContext getContext()
  {
    return getContext(Thread.currentThread().getContextClassLoader());
  }

  /**
   * Returns the context implementation.
   */
  protected MBeanContext getExistingContext()
  {
    return getExistingContext(Thread.currentThread().getContextClassLoader());
  }

  /**
   * Returns the context implementation.
   */
  protected MBeanContext getGlobalContext()
  {
    return getContext(ClassLoader.getSystemClassLoader());
  }

  /**
   * Returns the context implementation.
   */
  abstract protected MBeanContext getContext(ClassLoader loader);

  /**
   * Returns the context implementation.
   */
  abstract protected MBeanContext getExistingContext(ClassLoader loader);

  /**
   * Removes the context implementation.
   */
  protected void removeContext(MBeanContext context, ClassLoader loader)
  {
  }

  /**
   * Returns the view implementation.
   */
  protected MBeanView getView()
  {
    return getContext().getView();
  }

  /**
   * Returns the view implementation.
   */
  protected MBeanView getGlobalView()
  {
    return getGlobalContext().getView();
  }

  /**
   * Returns the view implementation.
   */
  protected MBeanView getParentView()
  {
    return null;
  }

  /**
   * Instantiate an MBean object to be registered with the server.
   *
   * @param className the className to be instantiated.
   *
   * @return the instantiated object.
   */
  public Object instantiate(String className)
    throws ReflectionException, MBeanException
  {
    try {
      Class cl = getClassLoaderRepository().loadClass(className);

      return cl.newInstance();
    } catch (ClassNotFoundException e) {
      throw new ReflectionException(e);
    } catch (InstantiationException e) {
      throw new ReflectionException(e);
    } catch (ExceptionInInitializerError e) {
      Throwable cause = e.getCause();
      if (cause instanceof Exception)
        throw new MBeanException((Exception) cause);
      else
        throw e;
    } catch (IllegalAccessException e) {
      throw new ReflectionException(e);
    }
  }

  /**
   * Instantiate an MBean object to be registered with the server.
   *
   * @param className the className to be instantiated.
   * @param loaderName names the classloader to be used
   *
   * @return the instantiated object.
   */
  public Object instantiate(String className, ObjectName loaderName)
    throws ReflectionException, MBeanException, InstanceNotFoundException
  {
    MBeanWrapper mbean = getMBean(loaderName);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(loaderName));
    else if (! (mbean.getObject() instanceof ClassLoader))
      throw new InstanceNotFoundException(L.l("{0} is not a class loader",
                                              loaderName));

    try {
      ClassLoader loader = (ClassLoader) mbean.getObject();

      Class cl = loader.loadClass(className);

      return cl.newInstance();
    } catch (ClassNotFoundException e) {
      throw new ReflectionException(e);
    } catch (InstantiationException e) {
      throw new ReflectionException(e);
    } catch (IllegalAccessException e) {
      throw new ReflectionException(e);
    }
  }

  /**
   * Instantiate an MBean object with the given arguments to be
   * passed to the constructor.
   *
   * @param className the className to be instantiated.
   * @param params the parameters for the constructor.
   * @param signature the signature of the constructor
   *
   * @return the instantiated object.
   */
  public Object instantiate(String className,
                            Object []params, String []signature)
    throws ReflectionException, MBeanException
  {
    try {
      Class cl = getClassLoaderRepository().loadClass(className);

      Constructor constructor = getConstructor(cl, signature);

      return constructor.newInstance(params);
    } catch (ClassNotFoundException e) {
      throw new ReflectionException(e);
    } catch (InstantiationException e) {
      throw new ReflectionException(e);
    } catch (InvocationTargetException e) {
        throw new MBeanException(e);
    } catch (IllegalAccessException e) {
      throw new ReflectionException(e);
    }
  }

  /**
   * Instantiate an MBean object with the given arguments to be
   * passed to the constructor.
   *
   * @param className the className to be instantiated.
   * @param loaderName names the classloader to be used
   * @param params the parameters for the constructor.
   * @param signature the signature of the constructor
   *
   * @return the instantiated object.
   */
  public Object instantiate(String className, ObjectName loaderName,
                            Object []params, String []signature)
    throws ReflectionException, MBeanException, InstanceNotFoundException
  {
    MBeanWrapper mbean = getMBean(loaderName);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(loaderName));
    else if (! (mbean.getObject() instanceof ClassLoader))
      throw new InstanceNotFoundException(L.l("{0} is not a class loader",
                                              loaderName));

    try {
      ClassLoader loader = (ClassLoader) mbean.getObject();

      Class cl = loader.loadClass(className);

      Constructor constructor = getConstructor(cl, signature);

      return constructor.newInstance(params);
    } catch (ClassNotFoundException e) {
      throw new ReflectionException(e);
    } catch (InstantiationException e) {
      throw new ReflectionException(e);
    } catch (InvocationTargetException e) {
        throw new MBeanException(e);
    } catch (IllegalAccessException e) {
      throw new ReflectionException(e);
    }
  }

  /**
   * Returns the class's constructor with the matching sig.
   */
  private Constructor getConstructor(Class cl, String []sig)
  {
    Constructor []constructors = cl.getConstructors();

    for (int i = 0; i < constructors.length; i++) {
      if (! Modifier.isPublic(constructors[i].getModifiers()))
        continue;

      if (isMatch(constructors[i].getParameterTypes(), sig))
        return constructors[i];
    }

    return null;
  }

  /**
   * Matches the parameters the sig.
   */
  private boolean isMatch(Class []param, String []sig)
  {
    if (param.length != sig.length)
      return false;

    for (int i = 0; i < param.length; i++) {
      if (! param[i].getName().equals(sig[i]))
        return false;
    }

    return true;
  }

  /**
   * Instantiate and register an MBean.
   *
   * @param className the className to be instantiated.
   * @param name the name of the mbean.
   *
   * @return the instantiated object.
   */
  public ObjectInstance createMBean(String className, ObjectName name)
    throws ReflectionException, InstanceAlreadyExistsException,
           MBeanException, NotCompliantMBeanException
  {
    return registerMBean(instantiate(className), name);
  }


  /**
   * Instantiate and register an MBean.
   *
   * @param className the className to be instantiated.
   * @param name the name of the mbean.
   * @param loaderName the name of the class loader to user
   *
   * @return the instantiated object.
   */
  public ObjectInstance createMBean(String className, ObjectName name,
                                    ObjectName loaderName)
    throws ReflectionException, InstanceAlreadyExistsException,
           MBeanException, NotCompliantMBeanException, InstanceNotFoundException
  {
    return registerMBean(instantiate(className, loaderName), name);
  }

  /**
   * Instantiate and register an MBean.
   *
   * @param className the className to be instantiated.
   * @param name the name of the mbean.
   * @param params the parameters for the constructor.
   * @param signature the signature of the constructor
   *
   * @return the instantiated object.
   */
  public ObjectInstance createMBean(String className, ObjectName name,
                                    Object []params, String []signature)
    throws ReflectionException, InstanceAlreadyExistsException,
           MBeanException, NotCompliantMBeanException
  {
    return registerMBean(instantiate(className, params, signature),
                         name);
  }

  /**
   * Instantiate and register an MBean.
   *
   * @param className the className to be instantiated.
   * @param name the name of the mbean.
   * @param loaderName the loader name for the mbean.
   * @param params the parameters for the constructor.
   * @param signature the signature of the constructor
   *
   * @return the instantiated object.
   */
  public ObjectInstance createMBean(String className, ObjectName name,
                                    ObjectName loaderName,
                                    Object []params, String []signature)
    throws ReflectionException, InstanceAlreadyExistsException,
           MBeanException, NotCompliantMBeanException, InstanceNotFoundException
  {
    return registerMBean(instantiate(className, loaderName, params, signature),
                         name);
  }

  /**
   * Registers an MBean with the server.
   *
   * @param object the object to be registered as an MBean
   * @param name the name of the mbean.
   *
   * @return the instantiated object.
   */
  public ObjectInstance registerMBean(Object object, ObjectName name)
    throws InstanceAlreadyExistsException,
           MBeanRegistrationException,
           NotCompliantMBeanException
  {
    if (object == null)
      throw new NullPointerException();

    MBeanContext context = getContext();

    if (context.getMBean(name) != null) {
      throw new InstanceAlreadyExistsException(String.valueOf(name));
    }

    DynamicMBean dynMBean = createMBean(object, name);

    if (object instanceof IntrospectionMBean)
      object = ((IntrospectionMBean) object).getImplementation();
    else if (object instanceof StandardMBean) {
      object = ((StandardMBean) object).getImplementation();
    }

    MBeanWrapper mbean = new MBeanWrapper(context, name, object, dynMBean);

    return context.registerMBean(mbean, name);
  }

  /**
   * Creates the dynamic mbean.
   */
  private DynamicMBean createMBean(Object obj, ObjectName name)
    throws NotCompliantMBeanException
  {
    if (obj == null)
      throw new NotCompliantMBeanException(L.l("{0} mbean is null", name));
    else if (obj instanceof DynamicMBean)
      return (DynamicMBean) obj;

    Class ifc = getMBeanInterface(obj.getClass());

    if (ifc == null)
      throw new NotCompliantMBeanException(L.l("{0} mbean has no MBean interface", name));

    return new IntrospectionMBean(obj, ifc);
  }

  /**
   * Returns the mbean interface.
   */
  private Class getMBeanInterface(Class cl)
  {
    for (; cl != null; cl = cl.getSuperclass()) {
      Class []interfaces = cl.getInterfaces();

      String mbeanName = cl.getName() + "MBean";
      String mxbeanName = cl.getName() + "MXBean";

      int p = mbeanName.lastIndexOf('.');
      mbeanName = mbeanName.substring(p);

      p = mxbeanName.lastIndexOf('.');
      mxbeanName = mxbeanName.substring(p);

      for (int i = 0; i < interfaces.length; i++) {
        Class ifc = interfaces[i];

        if (ifc.getName().endsWith(mbeanName)
	    || ifc.getName().endsWith(mxbeanName))
          return ifc;
      }
    }

    return null;
  }

  /**
   * Unregisters an MBean from the server.
   *
   * @param name the name of the mbean.
   */
  public void unregisterMBean(ObjectName name)
    throws InstanceNotFoundException,
           MBeanRegistrationException
  {
    MBeanContext context = getExistingContext();

    if (context != null) {
      context.unregisterMBean(name);

      log.finer("unregistered " + name);
    }

    // XXX: getDelegate().sendUnregisterNotification(name);
  }

  /**
   * Returns the MBean registered with the given name.
   *
   * @param name the name of the mbean.
   *
   * @return the matching mbean object.
   */
  public ObjectInstance getObjectInstance(ObjectName name)
    throws InstanceNotFoundException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    return mbean.getObjectInstance();
  }

  /**
   * Returns a set of MBeans matching the query.
   *
   * @param name the name of the mbean to match.
   * @param query the queryd to match.
   *
   * @return the set of matching mbean object.
   */
  public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query)
  {
    try {
      if (query != null) {
        query.setMBeanServer(this);
      }

      return getView().queryMBeans(name, query);
    } catch (Exception e) {
      log.log(Level.WARNING, e.toString(), e);

      return null;
    }
  }

  /**
   * Returns a set of names for MBeans matching the query.
   *
   * @param name the name of the mbean to match.
   * @param query the query to match.
   *
   * @return the set of matching mbean names.
   */
  public Set<ObjectName> queryNames(ObjectName name, QueryExp query)
  {
    try {
      if (query != null) {
        query.setMBeanServer(this);
      }

      return getView().queryNames(name, query);
    } catch (Exception e) {
      log.log(Level.WARNING, e.toString(), e);

      return null;
    }
  }

  /**
   * Tests if the name matches.
   */
  private boolean isMatch(ObjectName testName,
                          ObjectName queryName,
                          QueryExp query)
  {
    if (queryName == null)
      return true;

    if (! queryName.isPattern() &&
        ! testName.getDomain().equals(queryName.getDomain()))
      return false;

    if (queryName.isPropertyPattern()) {
      // If the queryName has a '*' in the properties, then check
      // the queryName properties to see if they match

      Hashtable map = queryName.getKeyPropertyList();
      Iterator iter = map.keySet().iterator();
      while (iter.hasNext()) {
        String key = (String) iter.next();
        String value = (String) map.get(key);

        if (! value.equals(testName.getKeyProperty(key)))
          return false;
      }
    }
    else {
      String testProps = testName.getCanonicalKeyPropertyListString();
      String queryProps = queryName.getCanonicalKeyPropertyListString();

      if (! testProps.equals(queryProps))
        return false;
    }

    return true;
  }

  /**
   * Returns true if the given object is registered with the server.
   *
   * @param name the name of the mbean to test.
   *
   * @return true if the object is registered.
   */
  public boolean isRegistered(ObjectName name)
  {
    return getView().getMBean(name) != null;
  }

  /**
   * Returns the number of MBeans registered.
   *
   * @return the number of registered mbeans.
   */
  public Integer getMBeanCount()
  {
    return new Integer(getView().getMBeanCount());
  }

  /**
   * Returns a specific attribute of a named MBean.
   *
   * @param name the name of the mbean to test
   * @param attribute the name of the attribute to retrieve
   *
   * @return the attribute value
   */
  public Object getAttribute(ObjectName name, String attribute)
    throws MBeanException, AttributeNotFoundException,
           InstanceNotFoundException, ReflectionException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null) {
      throw new InstanceNotFoundException(String.valueOf(name));
    }

    return mbean.getAttribute(attribute);
  }

  /**
   * Returns a list of several MBean attributes.
   *
   * @param name the name of the mbean
   * @param attributes the name of the attributes to retrieve
   *
   * @return the attribute value
   */
  public AttributeList getAttributes(ObjectName name, String []attributes)
    throws InstanceNotFoundException, ReflectionException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    return mbean.getAttributes(attributes);
  }

  /**
   * Sets an attribute in the MBean.
   *
   * @param name the name of the mbean
   * @param attribute the name/value of the attribute to set.
   */
  public void setAttribute(ObjectName name, Attribute attribute)
    throws InstanceNotFoundException, AttributeNotFoundException,
           InvalidAttributeValueException, MBeanException, ReflectionException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    mbean.setAttribute(attribute);
  }

  /**
   * Set an attributes in the MBean.
   *
   * @param name the name of the mbean
   * @param attributes the name/value list of the attribute to set.
   */
  public AttributeList setAttributes(ObjectName name, AttributeList attributes)
    throws InstanceNotFoundException, ReflectionException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    return mbean.setAttributes(attributes);
  }

  /**
   * Invokers an operation on an MBean.
   *
   * @param name the name of the mbean
   * @param operationName the name of the method to invoke
   * @param params the parameters for the invocation
   * @param signature the signature of the operation
   */
  public Object invoke(ObjectName name,
                       String operationName,
                       Object []params,
                       String []signature)
    throws InstanceNotFoundException, MBeanException, ReflectionException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    return mbean.invoke(operationName, params, signature);
  }

  /**
   * Returns the default domain for naming the MBean
   */
  public String getDefaultDomain()
  {
    return _defaultDomain;
  }

  /**
   * Adds a listener to a registered MBean
   *
   * @param name the name of the mbean
   * @param listener the listener object
   * @param filter filters events the listener is interested in
   * @param handback context to be returned to the listener
   */
  public void addNotificationListener(ObjectName name,
                                      NotificationListener listener,
                                      NotificationFilter filter,
                                      Object handback)
    throws InstanceNotFoundException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    mbean.addNotificationListener(listener, filter, handback);

    getContext().addNotificationListener(name, listener, filter, handback);
  }

  /**
   * Adds a listener to a registered MBean
   *
   * @param name the name of the mbean
   * @param listenerName the name of the listener
   * @param filter filters events the listener is interested in
   * @param handback context to be returned to the listener
   */
  public void addNotificationListener(ObjectName name,
                                      ObjectName listenerName,
                                      NotificationFilter filter,
                                      Object handback)
    throws InstanceNotFoundException
  {
    MBeanWrapper listenerMBean = getMBean(listenerName);

    if (listenerMBean == null)
      throw new InstanceNotFoundException(String.valueOf(listenerName));

    NotificationListener listener = listenerMBean.getListener();

    if (listener == null) {
      IllegalArgumentException exn = new IllegalArgumentException(L.l("{0} does not implement NotificationListener.", listenerName));
      throw new RuntimeOperationsException(exn);
    }

    addNotificationListener(name, listener, filter, handback);
  }

  /**
   * Removes a listener from a registered MBean
   *
   * @param name the name of the mbean
   * @param listener the listener object
   */
  public void removeNotificationListener(ObjectName name,
                                         NotificationListener listener)
    throws InstanceNotFoundException, ListenerNotFoundException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    mbean.removeNotificationListener(listener);

    getContext().removeNotificationListener(name, listener);
  }

  /**
   * Removes a listener from a registered MBean
   *
   * @param name the name of the mbean
   * @param listenerName the name of the listener
   */
  public void removeNotificationListener(ObjectName name,
                                         ObjectName listenerName)
    throws InstanceNotFoundException, ListenerNotFoundException
  {
    MBeanWrapper listenerMBean = getMBean(listenerName);

    if (listenerMBean == null)
      throw new InstanceNotFoundException(String.valueOf(listenerName));

    NotificationListener listener = listenerMBean.getListener();

    if (listener == null) {
      IllegalArgumentException exn = new IllegalArgumentException(L.l("{0} does not implement NotificationListener."));
      throw new RuntimeOperationsException(exn);
    }

    removeNotificationListener(name, listener);
  }

  /**
   * Removes a listener from a registered MBean
   *
   * @param name the name of the mbean
   * @param listenerName the name of the listener
   * @param filter the notification filter
   * @param handback context to the listener
   *
   * @since JMX 1.2
   */
  public void removeNotificationListener(ObjectName name,
                                         ObjectName listenerName,
                                         NotificationFilter filter,
                                         Object handback)
    throws InstanceNotFoundException, ListenerNotFoundException
  {
    MBeanWrapper listenerMBean = getMBean(listenerName);

    if (listenerMBean == null)
      throw new InstanceNotFoundException(String.valueOf(listenerName));

    NotificationListener listener = listenerMBean.getListener();

    if (listener == null) {
      IllegalArgumentException exn = new IllegalArgumentException(L.l("{0} does not implement NotificationListener."));
      throw new RuntimeOperationsException(exn);
    }

    removeNotificationListener(name, listener, filter, handback);
  }

  /**
   * Removes a listener from a registered MBean
   *
   * @param name the name of the mbean
   * @param listenerName the name of the listener
   * @param filter the notification filter
   * @param handback context to the listener
   *
   * @since JMX 1.2
   */
  public void removeNotificationListener(ObjectName name,
                                         NotificationListener listener,
                                         NotificationFilter filter,
                                         Object handback)
    throws InstanceNotFoundException, ListenerNotFoundException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    getContext().removeNotificationListener(name, listener, filter, handback);

    mbean.removeNotificationListener(listener, filter, handback);
  }

  /**
   * Returns the analyzed information for an MBean
   *
   * @param name the name of the mbean
   *
   * @return the introspected information
   */
  public MBeanInfo getMBeanInfo(ObjectName name)
    throws InstanceNotFoundException, IntrospectionException,
           ReflectionException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    MBeanInfo info = mbean.getMBeanInfo();

    return info;
  }

  /**
   * Returns true if the MBean is an instance of the specified class.
   *
   * @param name the name of the mbean
   * @param className the className to test.
   *
   * @return true if the bean is an instance
   */
  public boolean isInstanceOf(ObjectName name, String className)
    throws InstanceNotFoundException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    Object obj = mbean.getObject();
    Class cl = obj.getClass();

    return isInstanceOf(cl, className);
  }

  private boolean isInstanceOf(Class cl, String className)
  {
    if (cl == null)
      return false;

    if (cl.getName().equals(className))
      return true;

    if (isInstanceOf(cl.getSuperclass(), className))
      return true;

    Class []ifs = cl.getInterfaces();
    for (int i = 0; i < ifs.length; i++) {
      if (isInstanceOf(ifs[i], className))
        return true;
    }

    return false;
  }

  /**
   * Returns the ClassLoader that was used for loading the MBean.
   *
   * @param mbeanName the name of the mbean
   *
   * @return the class loader
   *
   * @since JMX 1.2
   */
  public ClassLoader getClassLoaderFor(ObjectName name)
    throws InstanceNotFoundException
  {
    MBeanWrapper mbean = getMBean(name);

    if (mbean == null)
      throw new InstanceNotFoundException(String.valueOf(name));

    return mbean.getContext().getClassLoader();
  }

  /**
   * Returns the named ClassLoader.
   *
   * @param loaderName the name of the class loader
   *
   * @return the class loader
   *
   * @since JMX 1.2
   */
  public ClassLoader getClassLoader(ObjectName loaderName)
    throws InstanceNotFoundException
  {
    return null;
  }

  /**
   * Returns the ClassLoaderRepository for this MBeanServer
   *
   * @since JMX 1.2
   */
  public ClassLoaderRepository getClassLoaderRepository()
  {
    return getContext().getClassLoaderRepository();
  }

  /**
   * Deserializes a byte array in the class loader of the mbean.
   *
   * @param name the name of the mbean
   * @param data the data to deserialize
   *
   * @return the deserialization stream
   */
  public ObjectInputStream deserialize(ObjectName name, byte []data)
    throws InstanceNotFoundException, OperationsException
  {
    throw new UnsupportedOperationException();
  }

  /**
   * Deserializes a byte array in the class loader of the mbean.
   *
   * @param className the className matches to the loader
   * @param data the data to deserialize
   *
   * @return the deserialization stream
   */
  public ObjectInputStream deserialize(String className, byte []data)
    throws OperationsException, ReflectionException
  {
    throw new UnsupportedOperationException();
  }

  /**
   * Deserializes a byte array in the class loader of the mbean.
   *
   * @param className the className matches to the loader
   * @param loaderName the loader to use for deserialization
   * @param data the data to deserialize
   *
   * @return the deserialization stream
   */
  public ObjectInputStream deserialize(String className,
                                       ObjectName loaderName,
                                       byte []data)
    throws OperationsException, ReflectionException,
           InstanceNotFoundException
  {
    throw new UnsupportedOperationException();
  }

  /**
   * Returns the domains for all registered MBeans
   *
   * @since JMX 1.2
   */
  public String []getDomains()
  {
    return getView().getDomains();
  }

  /**
   * Finds the MBean implementation.
   */
  MBeanWrapper getMBean(ObjectName name)
  {
    return getView().getMBean(name);
  }

  /**
   * Handles the case where a class loader is dropped.
   */
  public void destroy()
  {
    try {
      MBeanServerFactory.releaseMBeanServer(this);
    } catch (IllegalArgumentException e) {
      log.log(Level.FINEST, e.toString(), e);
    } catch (Throwable e) {
      log.log(Level.FINER, e.toString(), e);
    }
  }

  /**
   * Returns the string form.
   */
  public String toString()
  {
    if (_defaultDomain != null)
      return "MBeanServerImpl[domain=" + _defaultDomain + "]";
    else
      return "MBeanServerImpl[]";
  }

  static {
    try {
      SERVER_DELEGATE_NAME = new ObjectName("JMImplementation:type=MBeanServerDelegate");
    } catch (Throwable e) {
      e.printStackTrace();
    }
  }
}
