/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.enterprise.admin.server.core.jmx;

//JDK imports
import java.lang.reflect.Method;
import java.util.Set;
import java.util.HashSet;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.util.logging.Level;
import java.util.logging.Logger;

//JMX imports 
import javax.management.MBeanServer;
import javax.management.QueryExp;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InvalidAttributeValueException;
import javax.management.InstanceNotFoundException;
import javax.management.AttributeNotFoundException;
import javax.management.ReflectionException;
import javax.management.MBeanException;
import javax.management.IntrospectionException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanInfo;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanRegistrationException;
import javax.management.NotificationListener;
import javax.management.NotificationFilter;
import javax.management.NotCompliantMBeanException;
import javax.management.OperationsException;
import javax.management.MBeanRegistrationException;
import javax.management.NotCompliantMBeanException;

import com.sun.enterprise.admin.meta.MBeanRegistry;
import com.sun.enterprise.admin.meta.MBeanRegistryFactory;
//server
import com.sun.enterprise.server.ApplicationServer;

//Admin imports
import com.sun.enterprise.admin.AdminContext;
import com.sun.enterprise.admin.common.ObjectNames;
import com.sun.enterprise.admin.common.ObjectNameHelper;
import com.sun.enterprise.admin.common.constant.AdminConstants;
import com.sun.enterprise.admin.common.exception.AFRuntimeStoreException;
import com.sun.enterprise.admin.common.exception.AFRuntimeException;
import com.sun.enterprise.admin.event.AdminEventCache;
import com.sun.enterprise.admin.util.proxy.ProxyFactory;

//i18n import
import com.sun.enterprise.util.i18n.StringManager;

import javax.management.MBeanServerFactory;
import javax.management.loading.ClassLoaderRepository;

import com.sun.enterprise.config.serverbeans.ServerHelper;

/* for checking persistent store */
import com.sun.enterprise.admin.server.core.jmx.storage.PersistenceChecker;
import com.sun.enterprise.admin.server.core.jmx.storage.MBeanManufacturer;
import com.sun.enterprise.server.ondemand.entry.*;

/** A class that is a poor man's interceptor. There are following reasons that led
 * to use this interceptor and not the standard JMX 1.2 interceptor.
 * <LI> Tight deadlines and smoke test has to work :( </LI>
 * <LI> We have lazy loading going on. (Again poor man's) This means that any 
 * MBean has to be registered first before it can be invoked. We did not have any
 * good solution at the architectural level for that, nor does JMX 1.2 have it. The
 * way we do it is to check that persistent medium and/or Java Beans derived from the dtd
 * and initialized from domain.xml (in memory) indicate that such an element should exist,
 * and then we register the mbean before invoking it. This solves the problem of
 * initialization meaning the MBeans for all the elements in configuration file (domain.xml)
 * do not have to be registered in order to invoke the operation. </LI>
 * <LI> Still, we want a JMX 1.2 implementation of the MBeanServer as some 
 * immediate future work may need it. </LI>
 * <LI> Ugliness to begin with gives birth to more ugliness. This class has
 * to handle the case of config files being hand edited. :((( </LI>
 * Hence this interceptor results. All it does is the following:
 * <LI> Implements the javax.management.MBeanServer as sincerely as possible. </LI>
 * <LI> Uses Lazy loading meaning that the MBeans will be registered if they
 * do not already exist in registry of the MBeanServer. An attempt will be made
 * to see that config bean already exists. (This is not a violation per se, but
 * an interpretation of the MBeanServer specification).
 * @see javax.management.MBeanServer
 * @since 8.0
*/

public class SunoneInterceptor implements MBeanServer, EntryPoint {
    
    public static final String  HOT_CONFIG_METHOD_NAME  = "canApplyConfigChanges";
    public static final String  FORCE_APPLY_METHOD_NAME = "overwriteConfigChanges";
    public static final String  APPLY_METHOD_NAME       = "applyConfigChanges";
    public static final String  USE_MANUAL_METHOD_NAME  = "useManualConfigChanges";
    public static final String  GET_HOST_AND_PORT_METHOD_NAME = "getHostAndPort";
    
   
    private static final Logger sLogger = 
            Logger.getLogger(AdminConstants.kLoggerName);
    private static StringManager localStrings =
		StringManager.getManager( SunoneInterceptor.class );
    private static AdminContext adminContext;

    private MBeanServer realMBeanServer;

    /**
        The private constructor so that only the factory method is called.
        Also makes sure that there is single instance of MBeanServer in the JVM.

        @param defaultDomainName represents the domain of the MBeanServer.
        @throws InitException in case the System Mbeans can't be registered. The
        System MBeans are ServerController and GenericConfigurator.
    */

    SunoneInterceptor(String defaultDomainName) throws InitException {
    }

    /**
     * Added new method. Description tBd
     */
    void setJmxMBeanServer(MBeanServer jmxMBS) throws InitException {
        realMBeanServer = (MBeanServer)ProxyFactory.createProxy(
                    MBeanServer.class, jmxMBS,
                    adminContext.getMBeanServerInterceptor());
        logMBeanServerInfo();
        initialize();   
    }
        
    /**
        The private constructor so that only the factory method is called.
        Also makes sure that there is single instance of MBeanServer in the JVM.
        The default domain of this MBeanServer will be "ias".

        @throws InitException in case the System Mbeans can't be registered. The
        System MBeans are ServerController and GenericConfigurator.
    */

    SunoneInterceptor() throws InitException {
        this(ServiceName.DOMAIN);
    }

    /**
        Factory method to generate the only instance of this class in the JVM.
        Makes sure that the constructor of this class is called exactly once.
        
        @return MBeanServer instance by calling private constructor.
        @throws InitException if the MBeanServer can not be initialized.
        @deprecated use  AppServerMBeanServerBuilder.getMBeanServerInstance(); 
    */
    
    public static MBeanServer getMBeanServerInstance() 
        throws InitException {
            return AppServerMBeanServerFactory.getMBeanServerInstance();
    }
    
    /**
     * Set admin context. 
     */
    public static void setAdminContext(AdminContext ctx) {
       adminContext = ctx;
    }

    /**
        Initializes the MBeanServer. This method registers the System MBeans.
        The System MBeans are assumed to have default constructor.
       
        @throws InitException if any of the System MBeans can't be initialized.
    */

    private void initialize() throws InitException {
        try {
            ObjectName controllerObjectName 
                    = ObjectNames.getControllerObjectName();
            ObjectName configObjectName     
                    = ObjectNames.getGenericConfiguratorObjectName();
            ObjectName[] objectNames        = { 
                controllerObjectName,
                configObjectName
            };
	    String controllerClassName = 
            "com.sun.enterprise.admin.server.core.mbean.config.ServerController";
	    String configClassName = 
            "com.sun.enterprise.admin.server.core.mbean.config.GenericConfigurator";
		String[] clNames = {controllerClassName, configClassName};
            for (int i = 0 ; i < clNames.length ; i++) {
                createAndRegister( clNames[i], objectNames[ i ] );
            }
            registerDottedNameSupport();
        }
        catch (Exception e) {
            throw new InitException(e.getMessage(), e.getCause() );
        }
    }
    
    /**
    	<LLC>
    	Load the MBeans which support dotted names.
     */
    
	// screwy build dependencies for this sort of lame instantation
	static private final String DottedMBeansIniterClassName	= 
			"com.sun.enterprise.admin.mbeans.DottedNameMBeansIniter";
			
		private void
	registerDottedNameSupport()
		throws Exception
	{
		final Class		initerClass	= Class.forName( DottedMBeansIniterClassName );
		
		// invoke new DottedNamesMBeanIniter( MBeanServer m )
		final Class []		signature	= new Class [] { MBeanServer.class };
		final java.lang.reflect.Constructor	constructor	= initerClass.getConstructor( signature );
		constructor.newInstance( new Object [] { this } );
		// done--it will have done its job
	}
	
	private ObjectInstance createAndRegister( String className, ObjectName objectName )
		throws	Exception
	{
		try
		{
			final Class  mbeanClass				= Class.forName( className );
			final Object mbeanImpl				= mbeanClass.newInstance();
			final ObjectInstance mbeanInstance	= this.registerMBean( mbeanImpl, objectName );
			
			sLogger.log(Level.FINE, "core.system_mbean_init_ok", objectName.toString() );
			return( mbeanInstance );
		}
		catch( Exception e )
		{
			e.printStackTrace();
			throw e;
		}
	}
	
    /* </LLC> */
    
    public ObjectInstance registerMBean(Object object, ObjectName objectName) 
        throws  InstanceAlreadyExistsException,
                MBeanRegistrationException,
                NotCompliantMBeanException {
        return (realMBeanServer.registerMBean(object, objectName));
    }

    public void generateEntryContext(Object obj) {
        ServerEntryHelper.generateMbeanEntryContext((ObjectName) obj);
    }
    
    
    public Object invoke(ObjectName objectName, String operationName, 
        Object[] params, String[] signature) 
        throws ReflectionException, InstanceNotFoundException, MBeanException {

        generateEntryContext(objectName);
	  
        if(isInstanceMBean(objectName) && isConfigCheckRequired(operationName)) {
            checkHotConfigChanges(objectName);
        }
        
         if(FORCE_APPLY_METHOD_NAME.equals(operationName)) {
             // Manual changes to config will be overwritten, so persist the
             // state restart is required.
             String instanceName =
                     ObjectNameHelper.getServerInstanceName(objectName);
             AdminEventCache cache = AdminEventCache.getInstance(instanceName);
             cache.setRestartNeeded(true);
            //operationName = APPLY_METHOD_NAME;
         }
        registerWithPersistenceCheck(objectName);
        //logMBeanInfo(objectName);
        Object actualResult = realMBeanServer.invoke(objectName, operationName,
            params, signature );
        return ( actualResult );
    }
    
    public Object getAttribute(ObjectName objectName, String attributeName) 
        throws InstanceNotFoundException, AttributeNotFoundException, 
               MBeanException, ReflectionException {
        if(isInstanceMBean(objectName)) {
            checkHotConfigChanges(objectName);
        }
        registerWithPersistenceCheck(objectName);
        //logMBeanInfo(objectName);
        Object value = realMBeanServer.getAttribute(objectName, attributeName);
        return ( value );
    }
    
    public void setAttribute(ObjectName objectName, Attribute attribute) throws 
        InstanceNotFoundException, AttributeNotFoundException, 
        MBeanException, ReflectionException, InvalidAttributeValueException {
        if(isInstanceMBean(objectName)) {
            checkHotConfigChanges(objectName);
        }
        registerWithPersistenceCheck(objectName);
        //logMBeanInfo(objectName);
        realMBeanServer.setAttribute(objectName, attribute);
    }

    public AttributeList  getAttributes(ObjectName objectName, String[] attrNames) 
        throws InstanceNotFoundException, ReflectionException {//, RuntimeOperationsException
//        if(isInstanceMBean(objectName)) 
        {
            checkHotConfigChanges(objectName);
        }
        
        registerWithPersistenceCheck(objectName);
        return ( realMBeanServer.getAttributes(objectName, attrNames) );
    }

    public AttributeList setAttributes (ObjectName objectName, AttributeList attributeList) 
            throws InstanceNotFoundException, ReflectionException {
        if(isInstanceMBean(objectName)) {
            checkHotConfigChanges(objectName);
        }

        registerWithPersistenceCheck(objectName);
        return ( realMBeanServer.setAttributes(objectName, attributeList) );
    }

    
    public void unregisterMBean(ObjectName objectName) 
        throws InstanceNotFoundException, MBeanRegistrationException {
        realMBeanServer.unregisterMBean(objectName);
    }
	
    public Integer getMBeanCount() {
	return ( realMBeanServer.getMBeanCount() );
    }

    public Set queryMBeans(ObjectName name, QueryExp exp) {
        registerConfigMBeans();
        return ( realMBeanServer.queryMBeans(name, exp) );
    }

    public MBeanInfo getMBeanInfo(ObjectName objName) throws
        InstanceNotFoundException, IntrospectionException, ReflectionException {
		registerWithPersistenceCheck(objName);
        return ( realMBeanServer.getMBeanInfo(objName) );
    }

    public boolean isRegistered(ObjectName name) {
       /*
        // This is the actual way to do it
        // since there is a bug in initializing mbeans
        // commenting this for now in the assumption that
        // that the bug will be fixed soon.
         
        boolean isRegistered = realMBeanServer.isRegistered(name);
        if(!isRegistered) {
            try {
                registerWithPersistenceCheck(name);
                isRegistered = true;
            } catch(Exception e) {
                //ignore
            }
        }
        return isRegistered;
        */
       return realMBeanServer.isRegistered(name); 
    }

    public void addNotificationListener(ObjectName objectName, 
        NotificationListener notificationListener, 
        NotificationFilter notificationFilter, Object obj) 
        throws InstanceNotFoundException {
        realMBeanServer.addNotificationListener(objectName, 
            notificationListener, notificationFilter, obj);
    }

    public void addNotificationListener (ObjectName objectName, 
        ObjectName objectName1, NotificationFilter notificationFilter, 
        Object obj) throws InstanceNotFoundException {
        realMBeanServer.addNotificationListener(objectName, objectName1,
        notificationFilter, obj);
    }

    public ObjectInstance createMBean (String str, ObjectName objectName) 
        throws ReflectionException, InstanceAlreadyExistsException, 
        MBeanRegistrationException, MBeanException, 
        NotCompliantMBeanException {
        return realMBeanServer.createMBean (str, objectName);
    }

    public ObjectInstance createMBean (String str, ObjectName objectName, 
        ObjectName objectName2) throws ReflectionException, 
        InstanceAlreadyExistsException, MBeanRegistrationException, 
        MBeanException, NotCompliantMBeanException, InstanceNotFoundException {
        return ( realMBeanServer.createMBean (str, objectName, objectName2) );
    }

    public ObjectInstance createMBean (String str, ObjectName objectName, 
        Object[] obj, String[] str3) 
        throws ReflectionException, InstanceAlreadyExistsException, 
        MBeanRegistrationException, MBeanException, NotCompliantMBeanException {
        return realMBeanServer.createMBean (str, objectName, obj, str3);
    }

    public ObjectInstance createMBean (String str, ObjectName objectName, 
        ObjectName objectName2, Object[] obj, String[] str4) 
        throws ReflectionException, InstanceAlreadyExistsException, 
        MBeanRegistrationException, MBeanException, 
        NotCompliantMBeanException, InstanceNotFoundException {
        return realMBeanServer.createMBean (str, objectName,
            objectName2, obj, str4);
    }

    /* deprecated API @since 1.1 - use with caution */
    public ObjectInputStream deserialize (String str, byte[] values) 
        throws OperationsException, ReflectionException {
        return realMBeanServer.deserialize (str, values);
    }

    /* deprecated API @since 1.1 - use with caution */
    public ObjectInputStream deserialize (ObjectName objectName, byte[] values) 
        throws InstanceNotFoundException, OperationsException {
        return realMBeanServer.deserialize (objectName, values);
    }

    public ObjectInputStream deserialize (String str, ObjectName objectName, 
        byte[] values) throws InstanceNotFoundException, OperationsException, 
        ReflectionException {
        return realMBeanServer.deserialize (str, objectName, values);
    }

    public String getDefaultDomain() {
        return realMBeanServer.getDefaultDomain();
    }
    
    public ObjectInstance getObjectInstance(ObjectName objectName)
        throws InstanceNotFoundException {
        return realMBeanServer.getObjectInstance(objectName);
    }
    
    public Object instantiate(String str) throws ReflectionException,
        MBeanException {
        return realMBeanServer.instantiate(str);
    }
    
    public Object instantiate(String str, ObjectName objectName)
    throws ReflectionException, MBeanException, InstanceNotFoundException {
        return realMBeanServer.instantiate(str, objectName);
    }
    
    public Object instantiate(String str, Object[] obj, String[] str2)
    throws ReflectionException, MBeanException {
        return realMBeanServer.instantiate(str, obj, str2);
    }
    
    public Object instantiate(String str, ObjectName objectName,
    Object[] obj, String[] str3)
    throws ReflectionException, MBeanException, InstanceNotFoundException {
        return realMBeanServer.instantiate(str, objectName, obj, str3);
    }

    public boolean isInstanceOf (ObjectName objectName, String str) 
    throws InstanceNotFoundException {
            return realMBeanServer.isInstanceOf(objectName, str);
    }

    public Set queryNames (ObjectName objectName, QueryExp queryExp) {
        registerConfigMBeans();
        return realMBeanServer.queryNames(objectName, queryExp);
    }

    public void removeNotificationListener (ObjectName objectName, 
            ObjectName objectName1) 
            throws InstanceNotFoundException, ListenerNotFoundException {
            realMBeanServer.removeNotificationListener (objectName, 
                objectName1);
    }

    public void removeNotificationListener (ObjectName objectName, 
            NotificationListener notificationListener) 
            throws InstanceNotFoundException, ListenerNotFoundException {
            realMBeanServer.removeNotificationListener (objectName, 
                notificationListener);
    }

    // START BACKUP_HOT ISSUE FIX APIs
        
    private void checkHotConfigChanges(ObjectName mbeanName) {
        //different case
        try {
            //check whether the mbeanName pertains to a server instance.
//            String instanceName = ObjectNameHelper.getServerInstanceName(mbeanName);
String instanceName = ApplicationServer.getServerContext().getInstanceName();
            ObjectName instanceObjectName = ObjectNames.getServerInstanceObjectName(instanceName);
            Object canApply = this.invoke(instanceObjectName, HOT_CONFIG_METHOD_NAME,
                null, null);
            //System.out.println("return value" + canApply);
            if (canApply.equals(Boolean.FALSE)) {
				String msg = localStrings.getString(
                       "admin.server.core.jmx.configuration_changed_apply_changes",
                       instanceName);
                throw new AFRuntimeStoreException( msg );
            }
        } catch(AFRuntimeStoreException af) { 
            throw af;
        } catch (Exception e) {
			String msg = localStrings.getString( "admin.server.core.jmx.bad_server_configuration" );
            sLogger.log(Level.INFO, msg, e);
            throw new AFRuntimeException( msg, e );
        }
    }

    private boolean isInstanceMBean(ObjectName mbeanName) {
        if(ObjectNameHelper.getServerInstanceName(mbeanName) != null) { 
            return true;
        } else {
            return false;
        }
    }
    
    private boolean isConfigCheckRequired(String operationName) {
        //System.out.println("Entering isConfigCheckRequired:" + operationName);
        
        if(GET_HOST_AND_PORT_METHOD_NAME.equals(operationName)) {
            //System.out.println("getHostAndPort: returning FALSE");
            return false;
        }
        
        if(FORCE_APPLY_METHOD_NAME.equals(operationName)) {
                //System.out.println("overwriteConfigChanges: returning FALSE");
                return false;
        }
        
        if(USE_MANUAL_METHOD_NAME.equals(operationName)) {
            //System.out.println("useManualConfigChanges: returning FALSE");
            return false;
        }
        
       
        if(HOT_CONFIG_METHOD_NAME.equals(operationName)) {
            return false;
        }
        
        //START: Optimization to prevent multiple checks for changes
        if("getMBeanInfo".equals(operationName)) {
            return false;
        }
        //END: Optimization to prevent multiple checks for changes
         
        //System.out.println("RETURNING true");
        return true;
    }
    
    
    /**
     * There are many cases where we need to perform lazy instantiation before
     * accessing the real mbean server. Currently only getClassLoader and getClassLoaderFor
     * do this now, but FIXTHIS eventually all code that accesses and object name, throws
     * an instanceNotFound exception and accesses the realMBeanServer should do so using
     *this convenience method to ensure that lazy instantiation happens properly.
     */
    private MBeanServer getRealMBeanServerAndInstantiate(ObjectName objectName) throws InstanceNotFoundException
    {
        registerWithPersistenceCheck(objectName);
        return realMBeanServer;
    }
       
    public ClassLoader getClassLoader(ObjectName objectName) 
        throws InstanceNotFoundException {
           return ( getRealMBeanServerAndInstantiate(objectName).getClassLoader(objectName) );
    }    
    
    public ClassLoader getClassLoaderFor(ObjectName objectName) 
        throws InstanceNotFoundException {
            return ( getRealMBeanServerAndInstantiate(objectName).getClassLoaderFor(objectName) );
            
    }
    
    public ClassLoaderRepository getClassLoaderRepository() {
		return ( realMBeanServer.getClassLoaderRepository() );
    }
    
    public String[] getDomains() {
        return ( realMBeanServer.getDomains() );
    }
      
    public void removeNotificationListener(ObjectName objectName, 
        NotificationListener notificationListener, NotificationFilter 
        notificationFilter, Object obj) throws InstanceNotFoundException, 
        ListenerNotFoundException {
            realMBeanServer.
                removeNotificationListener(objectName, notificationListener,
                notificationFilter, obj);
    }
    
    public void removeNotificationListener(ObjectName objectName, 
        ObjectName objectName1, NotificationFilter notificationFilter, 
        Object obj) 
        throws InstanceNotFoundException, ListenerNotFoundException {
            realMBeanServer.removeNotificationListener(objectName, objectName1,
                notificationFilter, obj);
    }
    
    /** Logs the MBeanServer information. It is logged to the server's output/error
     * log, to convey the information about MBeanServer implementation used.
     * @throws RuntimeException if there are some problems in invoking 
     * implementation methods.
    */
    private void logMBeanServerInfo() {
        try {
            final String        name  = "JMImplementation:type=MBeanServerDelegate";
            final ObjectName    oName = new ObjectName(name);
            
            sLogger.log(Level.FINE, "core.mbs_info");
            //log the implementation name
            String attrName     = "ImplementationName";
            String result       = (String) realMBeanServer.
                    getAttribute(oName, attrName);
            sLogger.log(Level.FINE, "core.mbs_implementation", result);
            //log the implementation vendor
            attrName    = "ImplementationVendor";
            result      = (String) realMBeanServer.getAttribute(oName, attrName);
            sLogger.log(Level.FINE, "core.mbs_vendor", result);
            //log the impl version
            attrName = "ImplementationVersion";
            result = (String) realMBeanServer.getAttribute(oName, attrName);
            sLogger.log(Level.FINE, "core.jmx_impl_version", result);
            //log the MBeanServerId
            attrName = "MBeanServerId";
            result = (String) realMBeanServer.getAttribute(oName, attrName);
            sLogger.log(Level.FINE, "core.mbs_id", result);
            result = realMBeanServer.getClass().getName();
            sLogger.log(Level.FINE, "core_mbs_classname", result);            
        }
        catch(Exception e) {
            throw new RuntimeException (e);
        }
    }
    /**
     * this method added to synchronize lazy loaded MBeans manufacturing and registering
     */
    synchronized private void manufactureAndRegisterMBean(ObjectName oName) throws Exception
    {
        //the "second" check inside synchronized area (it will perevent double manufacturing)
        if (realMBeanServer.isRegistered(oName) ) {
            //sLogger.log(Level.FINE, "core.mbean_exists", oName);
            return;  //already registered
        }
        //call to lazy loading here
        final Object product = manufactureMBean(oName);
        if(product==null)
        {
                String msg = localStrings.getString(
                       "admin.server.core.jmx.lazybean_not_found",
                       oName.toString());
                throw new InstanceNotFoundException(msg);
        }
        //register MBean.
        this.registerMBean(product, oName);
        sLogger.log(Level.FINE, "core.create_and_register", oName);
    }
    
    /** This method does the following in a sequential manner:
     * <LI> If the MBean with given ObjectName is registered, do nothing. </LI>
     * <LI> If the MBean does not exist, create the MBean by "manufacturing process". </LI>
     * <LI> If the MBean is thus manufactured, then register that MBean in MBeanServer. </LI>
     * @param oName ObjectName that corresponds to a single MBean - should not be a pattern.
     * @since 8.0.
     * @exception IllegalArgumentException if oName is a pattern.
     * @exception RuntimeException with actual exception embedded in case operation fails.
    */
    private void registerWithPersistenceCheck(ObjectName oName) throws InstanceNotFoundException
    {
           
        if (! realMBeanServer.isRegistered(oName) ) {
            try {
                //Manufacture the MBean now.
                manufactureAndRegisterMBean(oName);
            }
            catch (InstanceNotFoundException infe)
            {
                throw infe;
            }
            catch (RuntimeException re)
            {
                throw re;
            }
            catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        //else {
                //sLogger.log(Level.FINE, "core.mbean_exists", oName);
        //}
    }
    
    private Object manufactureMBean(ObjectName oName) throws InstanceNotFoundException
    {
        final PersistenceChecker checker         = new PersistenceChecker();
        checker.setAdminContext(adminContext);
        final Object             storedObject	 = checker.findElement(oName);
        Object                   match           = null;
        if (storedObject != null) {
            MBeanManufacturer producer = new MBeanManufacturer(oName, storedObject);
            producer.setAdminContext(adminContext);
            match = producer.createMBeanInstance();
        }
        else {
            //this is severe - storage is not in sync with whatever is requested to be done.
            sLogger.log(Level.FINEST, "core.not_in_config", oName);
        }
        //Actually it should be asserted that match is NOT null.
        return ( match );
    }
        
    public void shutdown() {
        MBeanServerFactory.releaseMBeanServer(realMBeanServer);
        sLogger.log(Level.FINE, "core.release_mbs");
    }
    
    /* comment out this method later on */
    private void logMBeanInfo(ObjectName oName) {
        
        // if we are not logging at the finest level, just return
        if (!sLogger.isLoggable(Level.FINEST)) {
            return;            
        }
        
        //This method assumes that the mbean is registered.
	MBeanInfo info = null;
	try {
	    info = realMBeanServer.getMBeanInfo(oName);
	} catch (Exception e) {
	    e.printStackTrace();
	    return;
	}
	sLogger.log(Level.FINEST,"\nCLASSNAME: \t"+ info.getClassName());
	sLogger.log(Level.FINEST,"\nDESCRIPTION: \t"+ info.getDescription());
	sLogger.log(Level.FINEST,"\nATTRIBUTES");
	MBeanAttributeInfo[] attrInfo = info.getAttributes();
	if (attrInfo.length>0) {
	    for(int i=0; i<attrInfo.length; i++) {
		sLogger.log(Level.FINEST," ** NAME: \t"+ attrInfo[i].getName());
		sLogger.log(Level.FINEST,"    DESCR: \t"+ attrInfo[i].getDescription());
		sLogger.log(Level.FINEST,"    TYPE: \t"+ attrInfo[i].getType() +
		     "\tREAD: "+ attrInfo[i].isReadable() +
		     "\tWRITE: "+ attrInfo[i].isWritable());
	    }
	} else 
            sLogger.log(Level.FINEST," ** No attributes **");
	sLogger.log(Level.FINEST,"\nCONSTRUCTORS");
	MBeanConstructorInfo[] constrInfo = info.getConstructors();
	for(int i=0; i<constrInfo.length; i++) {
	    sLogger.log(Level.FINEST," ** NAME: \t"+ constrInfo[i].getName());
	    sLogger.log(Level.FINEST,"    DESCR: \t"+ constrInfo[i].getDescription());
	    sLogger.log(Level.FINEST,"    PARAM: \t"+ constrInfo[i].getSignature().length +" parameter(s)");
	}
	sLogger.log(Level.FINEST,"\nOPERATIONS");
	MBeanOperationInfo[] opInfo = info.getOperations();
	if (opInfo.length>0) {
	    for(int i=0; i<opInfo.length; i++) {
		sLogger.log(Level.FINEST," ** NAME: \t"+ opInfo[i].getName());
		sLogger.log(Level.FINEST,"    DESCR: \t"+ opInfo[i].getDescription());
		sLogger.log(Level.FINEST,"    PARAM: \t"+ opInfo[i].getSignature().length +" parameter(s)");
	    }
	} else 
            sLogger.log(Level.FINEST," ** No operations ** ");
	sLogger.log(Level.FINEST,"\nNOTIFICATIONS");
	MBeanNotificationInfo[] notifInfo = info.getNotifications();
	if (notifInfo.length>0) {
	    for(int i=0; i<notifInfo.length; i++) {
		sLogger.log(Level.FINEST," ** NAME: \t"+ notifInfo[i].getName());
		sLogger.log(Level.FINEST,"    DESCR: \t"+ notifInfo[i].getDescription());
	    }
	} else 
             sLogger.log(Level.FINEST," ** No notifications **");
    }
    
    private static boolean _alreadyCalled = false;
    /**
     * NOTE: I am using runtime configcontext to instantiate configmbeans
     *
     * FIXME: The current implementation does not load all the mbeans
     * while creating elements. Hence, some mbeans may never be
     * loaded. This needs to be fixed.
     *
     * Note that the _alreadyCalled is being used to prevent
     * multiple calls to this method since it is expensive. Currently,
     * it takes about 4 seconds to register all config mbeans.
     *
     * FIXME: Eventual plan is to move this method to AdminService where
     * it is initialized once and there is no need to call again.
     *
     * This method does not throw any exception and is best-effort.
     */
    private void registerConfigMBeans() {
        if(!_alreadyCalled) //first try without synchronization
            registerConfigMBeansSynchro();
    }
    private synchronized void registerConfigMBeansSynchro() {
        
        //there is only 1 SunoneInterceptor
        //so, synchronize on this
/*        synchronized (this) {
            if(_alreadyCalled) 
                return;
            _alreadyCalled = true;
        }*/
        
        if(_alreadyCalled) 
            return;

        
        try {
            MBeanRegistry mr = MBeanRegistryFactory.getAdminMBeanRegistry();   
            mr.instantiateAndRegisterAllConfigMBeans(
                 adminContext.getAdminConfigContext(), 
                 ApplicationServer.getServerContext().getDefaultDomainName());
        } catch (Throwable t) {
             sLogger.log(Level.FINE, "Error in registering configMBeans", t);
        }
        _alreadyCalled = true;
    }
}
