/*
 *  Copyright 2008 biaoping.yin
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" bboss persistent,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.  
 */
package com.frameworkset.common.poolman.management;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.management.Attribute;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.RuntimeMBeanException;
import javax.management.RuntimeOperationsException;

import com.frameworkset.common.poolman.PoolManConstants;
import com.frameworkset.common.poolman.util.SQLManager;

public class JMXPoolDeployer extends BaseTableManager implements PoolManDeployer, Runnable,Serializable {

    private static MBeanServer server;

    /**
     * added paramater server which is getted from application server such
     * as tomcat,weblogic,websphere etc.
     */
    public JMXPoolDeployer(MBeanServer server) {
        super();
        if(server != null)
        {
            this.server = server;
        }
        // add VM shutdown event handler
        try {
            // use reflection and catch the Exception to allow PoolMan to work with 1.2 VM's
            Class r = Runtime.getRuntime().getClass();
            java.lang.reflect.Method m =
                    r.getDeclaredMethod("addShutdownHook", new Class[]{Thread.class});
            m.invoke(Runtime.getRuntime(), new Object[]{new Thread(this)});
        } catch (Exception e) {
        }
    }

    /** This Runnable is not started until a VM shutdown event is fired. */
    public void run() {
        try {
        	super.updateTableInfo();
            // Stop Pools
            ObjectName queryName = new ObjectName("*:*");
            Set allservices = server.queryNames(queryName, null);
            for (Iterator iter = allservices.iterator(); iter.hasNext();) {
                ObjectName objn = (ObjectName) iter.next();
                try {
                    server.invoke(objn, "stop", null, null);
                } catch (Exception re1) {
                }
            }

            // Externalize State
            // TO DO...

        } catch (Exception e) {
            System.out.println("Unable to properly shutdown: " + e);
        }
    }

    /** Load DataSource info from XML and create a Service for each entry set. */
    public void deployConfiguration(PoolManConfiguration config) throws Exception {

        try {

            // server
            if(server == null)
                JMXPoolDeployer.server = MBeanServerFactory.createMBeanServer();

            // classloader
            SQLManager manager = SQLManager.getInstance();
            ObjectName loaderName = new ObjectName("DefaultDomain:service=JMXPatchedClassLoader");
            /**
             * className - The class name of the MBean to be instantiated.
			 * name - The object name of the MBean. May be null.
			 * params - An array containing the parameters of the constructor to be invoked.
			 * signature - An array containing the signature of the constructor to be invoked.
             */
            server.createMBean("com.frameworkset.common.poolman.management.JMXClassLoader", loaderName, new Object[]{ manager.getClass().getClassLoader() }, new String[]{ "java.lang.ClassLoader" });

            ArrayList datasources = config.getDataSources();
            if (datasources != null) {

                for (Iterator iter = datasources.iterator(); iter.hasNext();) {

                    // Get each set of datasource entries
                    Properties dbprops = (Properties) iter.next();

                    // Create a DataSourceServiceMBean using those entries
                    ObjectInstance datasource = addDataSourceService(dbprops, loaderName);

                    // Start the new DataSource MBean
                    try {
                        server.invoke(datasource.getObjectName(), "start", new Object[0], new String[0]);
                    } catch (RuntimeMBeanException re) {
                        Exception e = re.getTargetException();
                        throw e;
                    }
                }

            }

            // start reflective Generic object pools

            ArrayList genericPools = config.getGenericPools();
            if (genericPools != null) {

                for (Iterator iter = genericPools.iterator(); iter.hasNext();) {

                    // Get each set of datasource entries
                    Properties props = (Properties) iter.next();

                    // Create a GenericPoolServiceMBean using those entries
                    ObjectInstance pooledObject = addPooledObjectService(props, loaderName);

                    // Start the new Service
                    try {
                        server.invoke(pooledObject.getObjectName(), "start", new Object[0], new String[0]);
                    } catch (RuntimeMBeanException re) {
                        Exception e = re.getTargetException();
                        System.err.println(e.getMessage());
                        e.printStackTrace();
                    }
                }
            }

            // start admin agent
            Properties adminProps = config.getAdminProperties();
            if (adminProps != null) {
                try {
                    Class agentType = Class.forName(adminProps.getProperty("class", "com.sun.jdmk.comm.HtmlAdaptorServer"));
                    Object agentInstance = agentType.newInstance();
                    String compositeName = new String(adminProps.getProperty("name", "Adaptor:name=html") + ",port=" + adminProps.getProperty("port", "8082"));
                    ObjectName agentName = new ObjectName(compositeName);
                    server.registerMBean(agentInstance, agentName);
                    java.lang.reflect.Method startMethod = agentType.getMethod("start", null);
                    startMethod.invoke(agentInstance, null);
                } catch (Exception e) {
                    System.err.println("ERROR: Could not create the Admin Agent: " + e);
                    e.printStackTrace();
                }
            }

        } catch (RuntimeOperationsException e) {
            throw e.getTargetException();
        } catch (MBeanException e) {
            throw e.getTargetException();
        } catch (RuntimeMBeanException e) {
            throw e.getTargetException();
        } catch (javax.management.RuntimeErrorException e) {
            throw e.getTargetError();
        } catch (Exception e) {
            throw e;
        }
    }

    private ObjectInstance addDataSourceService(Properties props, ObjectName loaderName) throws Exception {

        String dbname = null;
        try {
            dbname = (String) props.get("dbname");
        } catch (NullPointerException npe) {
            throw new Exception("No name found for database!");
        }

        // instance will have JNDI name of getName() and
        // ObjectName of getObjectName()
        ObjectName objectName = new ObjectName("DefaultDomain:service=PoolManDataSource-" + dbname);

        // class is same for all DataSource services
        String className = PoolManConstants.DATASOURCE_SVC_CLASSNAME;

        // create the instance, this binds it to the MBeanServer
        // with no attributes yet set
        ObjectInstance instance = null;
        try {
            instance = server.createMBean(className, objectName, loaderName);
        } catch (Exception ine) {
            ine.printStackTrace();
            //System.exit(0);
        }

        // here's the new MBean's info
        MBeanInfo mbeanInfo = server.getMBeanInfo(instance.getObjectName());

        // set attributes based on properties

        MBeanAttributeInfo[] attributes = mbeanInfo.getAttributes();
        for (int n = 0; n < attributes.length; n++) {

            // get MBean attribute name
            String attrName = attributes[n].getName();

            if (props.containsKey(attrName.toLowerCase())) {

                // get value in props
                String propsVal = props.getProperty(attrName.toLowerCase());

                // manage data type using java.bean package
                // so far we only need boolean, int, and String
                String datatype = attributes[n].getType();
                Class type;
                if (datatype.equals("int"))
                    type = Integer.TYPE;
                else if (datatype.equals("boolean"))
                    type = Boolean.TYPE;
                else
                    type = Class.forName(datatype);

                // create attribute value of correct type
                PropertyEditor editor = PropertyEditorManager.findEditor(type);
                editor.setAsText(propsVal);
                Object value = editor.getValue();
                Attribute attrValue = new Attribute(attrName, value);

                // set attribute value on mbean
                server.setAttribute(objectName, attrValue);

            }
        }

        return instance;
    }

    private ObjectInstance addPooledObjectService(Properties props, ObjectName loaderName) throws Exception {

        String poolName = null;
        try {
            poolName = (String) props.get("name");
        } catch (NullPointerException npe) {
            throw new Exception("No name found for ObjectPool!");
        }

        ObjectName objectName = new ObjectName("DefaultDomain:service=PoolManObjectPool-" + poolName);

        // class is same for all DataSource services
        String className = PoolManConstants.GENERIC_SVC_CLASSNAME;

        // create the instance, this binds it to the MBeanServer
        // with no attributes yet set (all set to default)
        ObjectInstance instance = server.createMBean(className, objectName, loaderName);

        // here's the new MBean's info
        MBeanInfo mbeanInfo = server.getMBeanInfo(instance.getObjectName());

        // set attributes based on properties

        MBeanAttributeInfo[] attributes = mbeanInfo.getAttributes();
        for (int n = 0; n < attributes.length; n++) {

            // get MBean attribute name
            String attrName = attributes[n].getName();

            if (props.containsKey(attrName.toLowerCase())) {

                // get value in props
                String propsVal = props.getProperty(attrName.toLowerCase());

                // manage data type using java.bean package
                // so far we only need boolean, int, and String
                String datatype = attributes[n].getType();
                Class type;
                if (datatype.equals("int"))
                    type = Integer.TYPE;
                else if (datatype.equals("boolean"))
                    type = Boolean.TYPE;
                else if (datatype.equals("float"))
                    type = Float.TYPE;
                else if (datatype.equals("long"))
                    type = Long.TYPE;
                else if (datatype.equals("double"))
                    type = Double.TYPE;
                else
                    type = Class.forName(datatype);

                // create attribute value of correct type
                PropertyEditor editor = PropertyEditorManager.findEditor(type);
                editor.setAsText(propsVal);
                Object value = editor.getValue();
                Attribute attrValue = new Attribute(attrName, value);

                // set attribute value on mbean
                server.setAttribute(objectName, attrValue);

            }

        }

        return instance;
    }

    public void removeDataSourceService(String dbname) throws Exception {

    }

	public void deployConfiguration(PoolManConfiguration config,
			Map<String, String> values) throws Exception {
		// TODO Auto-generated method stub
		
	}

	public void deployConfiguration(PoolManConfiguration config, String dbname)
			throws Exception {
		// TODO Auto-generated method stub
		
	}

}

