/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.system.server.jmx;

import java.io.ObjectInputStream;
import java.lang.reflect.Constructor;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanException;
import javax.management.NotCompliantMBeanException;
import javax.management.InstanceNotFoundException;
import javax.management.QueryExp;
import javax.management.AttributeNotFoundException;
import javax.management.AttributeList;
import javax.management.Attribute;
import javax.management.InvalidAttributeValueException;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanInfo;
import javax.management.IntrospectionException;
import javax.management.OperationsException;
import javax.management.MBeanServerDelegate;
import javax.management.loading.ClassLoaderRepository;

/**
 * An MBeanServer implementation that bridges between the platform MBeanServer
 * and the jboss implementation. This is used by the @{link MBeanServerBuilderImpl}
 * to bridge the period from the vm startup until the jboss server is loaded
 * and the jboss MBeanServer is available.
 * 
 * @author Scott.Stark@jboss.org
 * @version $Revision$
 */
public class LazyMBeanServer
   implements MBeanServer
{
   private static MBeanServer registeredServer;
   private static MBeanServer theServer;
   private static MBeanServer platformServer;
   private static String defaultDomain;
   private static MBeanServerDelegate theDelegate;

   /**
    * Called by the ServerImpl to reset theServer implementation to the jboss
    * version so that our xmbean and the like function correctly.
    * 
    * @param server - the existing MBeanServer
    * @return org.jboss.mx.server.MBeanServerImp
    * @throws Exception
    */ 
   public static MBeanServer resetToJBossServer(MBeanServer server)
      throws Exception
   {
      MBeanServer coreServer = server;
      if( theDelegate != null )
      {
         Class[] sig = {String.class, MBeanServer.class, MBeanServerDelegate.class};
         Object[] args = {defaultDomain, null, theDelegate};
         ClassLoader loader = Thread.currentThread().getContextClassLoader();
         Class c = loader.loadClass("org.jboss.mx.server.MBeanServerImpl");
         Constructor ctor = c.getConstructor(sig);
         theServer = (MBeanServer) ctor.newInstance(args);
         coreServer = theServer;
      }
      return coreServer;
   }
   /**
    * Provide access to the original MBeanServer that was registered so that
    * it can be removed on shutdown for example.
    * 
    * @param server - the ServerImple MBeanServer
    * @return either the argument, or the last LazyMBeanServer created
    */ 
   public static MBeanServer getRegisteredMBeanServer(MBeanServer server)
   {
      MBeanServer outerServer = server;
      if( registeredServer != null )
         outerServer = registeredServer;
      return outerServer;
   }

   LazyMBeanServer(String domain, MBeanServer outer,
      MBeanServerDelegate delegate)
   {
      defaultDomain = domain;
      platformServer = outer;
      theServer = outer;
      registeredServer = this;
      theDelegate = delegate;
   }

   public ObjectInstance createMBean(String className, ObjectName name)
      throws ReflectionException, InstanceAlreadyExistsException,
      MBeanRegistrationException, MBeanException,
      NotCompliantMBeanException
   {
      try
      {
         return createMBean(className, name, null);
      }
      catch (InstanceNotFoundException e)
      {
         throw new MBeanException(e);
      }
   }

   public ObjectInstance createMBean(String className, ObjectName name,
                 ObjectName loaderName) 
      throws ReflectionException, InstanceAlreadyExistsException,
      MBeanRegistrationException, MBeanException,
      NotCompliantMBeanException, InstanceNotFoundException
   {
      return getServer(name).createMBean(className, name, loaderName);
   }

   public ObjectInstance createMBean(String className, ObjectName name,
                 Object params[], String signature[]) 
      throws ReflectionException, InstanceAlreadyExistsException,
      MBeanRegistrationException, MBeanException,
      NotCompliantMBeanException
   {
      return getServer(name).createMBean(className, name, params, signature);
   }

   public ObjectInstance createMBean(String className, ObjectName name,
                 ObjectName loaderName, Object params[],
                 String signature[]) 
      throws ReflectionException, InstanceAlreadyExistsException,
      MBeanRegistrationException, MBeanException,
      NotCompliantMBeanException, InstanceNotFoundException
   {
      return getServer(name).createMBean(className, name, loaderName, params, signature);
   }

   public ObjectInstance registerMBean(Object object, ObjectName name)
      throws InstanceAlreadyExistsException, MBeanRegistrationException,
      NotCompliantMBeanException
   {
      return theServer.registerMBean(object, name);
   }

   public void unregisterMBean(ObjectName name)
      throws InstanceNotFoundException, MBeanRegistrationException
   {
      theServer.unregisterMBean(name);
   }

   public ObjectInstance getObjectInstance(ObjectName name)
      throws InstanceNotFoundException
   {
      ObjectInstance oi = getServer(name).getObjectInstance(name);
      return oi;
   }

   public Set queryMBeans(ObjectName name, QueryExp query)
   {
      // query both servers
      Set beans = platformServer.queryMBeans(name, query);
      Set beans2 = theServer.queryMBeans(name, query);
      beans.addAll(beans2);
      return beans;
   }

   public Set queryNames(ObjectName name, QueryExp query)
   {
      Set names = platformServer.queryNames(name, query);
      Set names2 = theServer.queryNames(name, query);
      names.addAll(names2);
      return names;
   }

   public boolean isRegistered(ObjectName name)
   {
      return getServer(name).isRegistered(name);
   }

   public Integer getMBeanCount()
   {
      return theServer.getMBeanCount();
   }

   public Object getAttribute(ObjectName name, String attribute)
      throws MBeanException, AttributeNotFoundException,
      InstanceNotFoundException, ReflectionException
   {
      return getServer(name).getAttribute(name, attribute);
   }

   public AttributeList getAttributes(ObjectName name, String[] attributes)
      throws InstanceNotFoundException, ReflectionException
   {
      return getServer(name).getAttributes(name, attributes);
   }

   public void setAttribute(ObjectName name, Attribute attribute)
      throws InstanceNotFoundException, AttributeNotFoundException,
      InvalidAttributeValueException, MBeanException, 
      ReflectionException
   {
      getServer(name).setAttribute(name, attribute);
   }

   public AttributeList setAttributes(ObjectName name,
                  AttributeList attributes)
  throws InstanceNotFoundException, ReflectionException
   {
      return getServer(name).setAttributes(name, attributes);
   }

   public Object invoke(ObjectName name, String operationName,
         Object params[], String signature[])
      throws InstanceNotFoundException, MBeanException,
      ReflectionException
   {
      Object value = getServer(name).invoke(name, operationName, params, signature);
      return value;
   }

   public String getDefaultDomain()
   {
      return theServer.getDefaultDomain();
   }

   public String[] getDomains()
   {
      return theServer.getDomains();
   }

   public void addNotificationListener(ObjectName name,
              NotificationListener listener,
              NotificationFilter filter,
              Object handback)
      throws InstanceNotFoundException
   {
      getServer(name).addNotificationListener(name, listener, filter, handback);
   }

   public void addNotificationListener(ObjectName name,
              ObjectName listener,
              NotificationFilter filter,
              Object handback)
      throws InstanceNotFoundException
   {
      getServer(name).addNotificationListener(name, listener, filter, handback);
   }

   public void removeNotificationListener(ObjectName name,
                 ObjectName listener) 
  throws InstanceNotFoundException, ListenerNotFoundException
   {
      getServer(name).removeNotificationListener(name, listener);
   }

   public void removeNotificationListener(ObjectName name,
                 ObjectName listener,
                 NotificationFilter filter,
                 Object handback)
      throws InstanceNotFoundException, ListenerNotFoundException
   {
      getServer(name).removeNotificationListener(name, listener, filter, handback);
   }

   public void removeNotificationListener(ObjectName name,
                 NotificationListener listener)
      throws InstanceNotFoundException, ListenerNotFoundException
   {
      getServer(name).removeNotificationListener(name, listener);
   }

   public void removeNotificationListener(ObjectName name,
                 NotificationListener listener,
                 NotificationFilter filter,
                 Object handback)
      throws InstanceNotFoundException, ListenerNotFoundException
   {
      getServer(name).removeNotificationListener(name, listener, filter, handback);
   }

   public MBeanInfo getMBeanInfo(ObjectName name)
      throws InstanceNotFoundException, IntrospectionException,
      ReflectionException
   {
      MBeanInfo info = getServer(name).getMBeanInfo(name);
      return info;
   }

   public boolean isInstanceOf(ObjectName name, String className)
      throws InstanceNotFoundException
   {
      return getServer(name).isInstanceOf(name, className);
   }

   public Object instantiate(String className)
      throws ReflectionException, MBeanException
   {
      return theServer.instantiate(className);
   }

   public Object instantiate(String className, ObjectName loaderName) 
      throws ReflectionException, MBeanException,
      InstanceNotFoundException
   {
      return theServer.instantiate(className, loaderName);
   }

   public Object instantiate(String className, Object params[],
              String signature[]) 
      throws ReflectionException, MBeanException
   {
      return theServer.instantiate(className, params, signature);
   }

   public Object instantiate(String className, ObjectName loaderName,
              Object params[], String signature[]) 
      throws ReflectionException, MBeanException,
      InstanceNotFoundException
   {
      return theServer.instantiate(className, loaderName, params, signature);
   }

   public ObjectInputStream deserialize(ObjectName name, byte[] data)
      throws InstanceNotFoundException, OperationsException
   {
      return getServer(name).deserialize(name, data);
   }

   public ObjectInputStream deserialize(String className, byte[] data)
      throws OperationsException, ReflectionException
   {
      return theServer.deserialize(className, data);
   }

   public ObjectInputStream deserialize(String className,
               ObjectName loaderName,
               byte[] data)
      throws InstanceNotFoundException, OperationsException,
      ReflectionException
   {
      return theServer.deserialize(className, loaderName, data);
   }

   public ClassLoader getClassLoaderFor(ObjectName mbeanName) 
      throws InstanceNotFoundException
   {
      return getServer(mbeanName).getClassLoaderFor(mbeanName);
   }

   public ClassLoader getClassLoader(ObjectName loaderName)
  throws InstanceNotFoundException
   {
      return getServer(loaderName).getClassLoader(loaderName);
   }

   public ClassLoaderRepository getClassLoaderRepository()
   {
      return theServer.getClassLoaderRepository();
   }

   /**
    * Choose the MBeanServer based on the ObjectName domain. The current
    * implementation chooses the platform MBeanServer for any domain starting
    * with "java".
    * 
    * @param name
    * @return Either the platform MBeanServer, or the jboss MBeanServer
    */ 
   private MBeanServer getServer(ObjectName name)
   {
      if( name != null && name.getDomain().startsWith("java") )
         return platformServer;
      return theServer;
   }
}
