/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2007-2009 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library 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 library 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 library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.commands.admin;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

/**
 * Utility class allowing MBean manipulations on the managed server.
 * @author Adriana.Danes@bull.net
 */
public class UtilAdmin {
    /**
     * Constant used in pattern ObjectNames.
     */
    public static final String ALL = ",*";
    public static final String J2EEServer = "J2EEServer";
    public static final String ServerProxy = "ServerProxy";
    public static final String J2EEType = "j2eeType";
    public static final String Type = "type";
    public static final String ServiceType = "service";

    /**
     * Private constructor for utility class.
     */
    private UtilAdmin() {
    }
    /**
     * Construct ObjectName for an MBean associated with a manageable object in a J2EEServer.
     * @param domain management domain name
     * @param server J2EE server name
     * @param type manageable object type
     * @return created ObjectName
     * @throws MalformedObjectNameException Could not create the ObjectName as
     */
    private static ObjectName getJ2eeMBean(final String domain, final String server, final String type) throws MalformedObjectNameException {
    	StringBuilder sb = new StringBuilder(domain);
        sb.append(":");
        sb.append(J2EEServer);
        sb.append("=");
        sb.append(server);
        sb.append(",");
        sb.append(J2EEType);
        sb.append("=");
        sb.append(type);
        sb.append(ALL);
        return ObjectName.getInstance(sb.toString());
    }

    /**
     * Construct ObjectName for an MBean associated with a manageable object in a J2EEServer.
     * @param domain management domain name
     * @param server J2EE server name
     * @param type manageable object type
     * @param name MBean name
     * @return created ObjectName
     * @throws MalformedObjectNameException Could not create the ObjectName as
     */
    protected static ObjectName getJ2eeMBean(final String domain, final String server, final String type, final String name) throws MalformedObjectNameException {
    	StringBuilder sb = new StringBuilder(domain);
        sb.append(":");
        sb.append(J2EEServer);
        sb.append("=");
        sb.append(server);
        sb.append(",");
        sb.append(J2EEType);
        sb.append("=");
        sb.append(type);
        sb.append(",");
        sb.append("name=");
        sb.append(name);
        return ObjectName.getInstance(sb.toString());
    }

    /**
     * Create ObjectName for J2EEServer MBean without knowing domain name
     * @param serverName the server name
     * @return ObjectName for J2EEServer MBean
     * @throws MalformedObjectNameException Format exception
     * @throws NullPointerException null parameter
     */
    public static ObjectName J2EEServer(final String serverName) throws MalformedObjectNameException, NullPointerException {
        StringBuilder sb = new StringBuilder("*");
        sb.append(":");
        sb.append(J2EEType);
        sb.append("=");
        sb.append(J2EEServer);
        sb.append(",");
        sb.append("name");
        sb.append("=");
        sb.append(serverName);
        return ObjectName.getInstance(sb.toString());
    }

    /**
     * Create ObjectName for ServerProxy MBean without knowing domain name
     * @param serverName the server name
     * @return ObjectName for ServerProxy MBean
     * @throws MalformedObjectNameException Format exception
     * @throws NullPointerException null parameter
     */
    public static ObjectName ServerProxy(final String serverName) throws MalformedObjectNameException, NullPointerException {
        StringBuilder sb = new StringBuilder("*");
        sb.append(":");
        sb.append(Type);
        sb.append("=");
        sb.append(ServerProxy);
        sb.append(",");
        sb.append("name");
        sb.append("=");
        sb.append(serverName);
        return ObjectName.getInstance(sb.toString());
    }

    private static ObjectName J2EEServer(final String domain, final String server)
        throws MalformedObjectNameException, NullPointerException {
        Hashtable<String, String> table = new Hashtable<String, String>();
        table.put(J2EEType, J2EEServer);
        table.put("name", server);
        return getObjectName(domain, table);
    }

    public static ObjectName getJonasServiceMBean(final String domain, final String server, final String service)
        throws MalformedObjectNameException, NullPointerException {
    	StringBuilder sb = new StringBuilder(domain);
        sb.append(":");
        sb.append(Type);
        sb.append("=");
        sb.append(ServiceType);
        sb.append(",");
        sb.append("name");
        sb.append("=");
        sb.append(service);
        sb.append(ALL);
        return ObjectName.getInstance(sb.toString());
    }

    public static ObjectName getJonasServiceMBean(final String domain,
                                                   final String server,
                                                   final String service,
                                                   final Properties keyProps)
        throws MalformedObjectNameException, NullPointerException {
        Hashtable<String, String> table = new Hashtable<String, String>();
        table.put(Type, "service");
        table.put("name", service);
        if (keyProps != null) {
            Enumeration keys = keyProps.keys();
            while (keys.hasMoreElements()) {
                String key = (String) keys.nextElement();
                String value = keyProps.getProperty(key);
                table.put(key, value);
            }
        }
        return getObjectName(domain, table);
    }

    /**
     * Create an ObjectName instance
     * @param domain management domain name
     * @param table key properties table
     * @return created ObjectName.
     * @throws NullPointerException One of the parameters is null.
     * @throws MalformedObjectNameException The domain contains an illegal character, or one of the keys or values in table contains
     * an illegal character, or one of the values in table does not follow the rules for quoting.
     */
    private static ObjectName getObjectName(final String domain,
            final Hashtable<String, String> table) throws MalformedObjectNameException, NullPointerException {
        return ObjectName.getInstance(domain, table);
    }
    /**
     * Get the beans name list in a given server
     * @param domain the server's domain
     * @param server the server name
     * @param conn the MBeanServer connection for this server
     * @return the list of the bean names
     * @throws MalformedObjectNameException should not arrive if we construct correct ObjectName
     * @throws IOException A communication problem occurred when talking to the MBean server
     */
    public static ArrayList<String> listBeans(final String domain, final String server, final MBeanServerConnection conn)
    throws MalformedObjectNameException, IOException {
        // Get EntityBeans ObjectNames
        String type = "EntityBean";
        ObjectName ons = getJ2eeMBean(domain, server, type);
        Set onSet = conn.queryNames(ons, null);
        // Get StatefulSessionBeans
        type = "StatefulSessionBean";
        ons = getJ2eeMBean(domain, server, type);
        Set nextSet = conn.queryNames(ons, null);
        onSet.addAll(nextSet);
        // Get StatelessSessionBeans
        type = "StatelessSessionBean";
        ons = getJ2eeMBean(domain, server, type);
        nextSet = conn.queryNames(ons, null);
        onSet.addAll(nextSet);
        // Get MessageDrivenBeans
        type = "MessageDrivenBean";
        ons = getJ2eeMBean(domain, server, type);
        nextSet = conn.queryNames(ons, null);
        onSet.addAll(nextSet);

        // The set contains all the xxBean ObjectNames
        Iterator<ObjectName> it = onSet.iterator();
        ArrayList<String> nameList = new ArrayList<String>();
        while (it.hasNext()) {
            ObjectName on = it.next();
            String name = on.getKeyProperty("name");
            String module = on.getKeyProperty("EJBModule");
            String printable = module.concat(": ");
            printable = printable.concat(name);
            nameList.add(printable);
        }
        return nameList;
    }

    /**
     * Get the modules name list in a given server
     * @param domain the server's domain
     * @param server the server name
     * @param conn the MBeanServer connection for this server
     * @return the list of the module names
     * @throws MalformedObjectNameException should not arrive if we construct correct ObjectName
     * @throws IOException A communication problem occurred when talking to the MBean server
     */
    protected static ArrayList<String> listModules(final String domain, final String server, final MBeanServerConnection conn)
    throws MalformedObjectNameException, IOException  {
        // Get EJBModules
        String type = "EJBModule";
        ObjectName ons = getJ2eeMBean(domain, server, type);
        Set onSet = conn.queryNames(ons, null);
        // Get WebModule
        type = "WebModule";
        ons = getJ2eeMBean(domain, server, type);
        Set nextSet = conn.queryNames(ons, null);
        onSet.addAll(nextSet);
        // Get ResourceAdapterModule
        type = "ResourceAdaperModule";
        nextSet = conn.queryNames(ons, null);
        onSet.addAll(nextSet);
        // Got set with the xxModule ObjectNames
        Iterator<ObjectName> it = onSet.iterator();
        ArrayList<String> nameList = new ArrayList<String>();
        while (it.hasNext()) {
            ObjectName on = it.next();
            String name = on.getKeyProperty("name");
            String appName = on.getKeyProperty("J2EEApplication");
            String printable = null;
            if (appName != null && appName.equals("none")) {
                printable = appName.concat(": ");
            } else {
                printable = "";
            }
            printable = printable.concat(name);
            nameList.add(printable);
        }
        return nameList;
    }

    /**
     * Get the application name list in a given server
     * @param domain the server's domain
     * @param server the server name
     * @param conn the MBeanServer connection for this server
     * @return the list of the application names
     * @throws MalformedObjectNameException should not arrive if we construct correct ObjectName
     * @throws IOException A communication problem occurred when talking to the MBean server
     */
    protected static ArrayList<String> listApps(final String domain, final String server, final MBeanServerConnection conn)
    throws MalformedObjectNameException, IOException  {
        // Get J2EEApplications
        // Get J2EEApplications
        String type = "J2EEApplication";
        ObjectName ons = getJ2eeMBean(domain, server, type);
        Set onSet = conn.queryNames(ons, null);
        Iterator<ObjectName> it = onSet.iterator();
        ArrayList<String> nameList = new ArrayList<String>();
        while (it.hasNext()) {
            ObjectName on = it.next();
            String name = on.getKeyProperty("name");
             nameList.add(name);
        }
        return nameList;
    }

    public static String dumpCustom(final String domain, final String server, final MBeanServerConnection conn) throws Exception {
        String ret = "";
        // Get jvm and os info
        ObjectName on = J2EEServer(domain, server);
        String jvmInfos = (String) conn.getAttribute(on, "jvmInfos");
        ret += jvmInfos;
        ret += "\n";
        // Get JTA info
        on = getJ2eeMBean(domain, server, "JTAResource", "JTAResource");
        if (conn.isRegistered(on)) {
            ret += "TM timeout=" + conn.getAttribute(on, "timeOut");
            ret += "\n";
        }
        // Get JDBCDataSource info
        ObjectName ons = getJ2eeMBean(domain, server, "JDBCDataSource");
        Set onSet = conn.queryNames(ons, null);
        if (!onSet.isEmpty()) {
            Iterator<ObjectName> it = onSet.iterator();
            while(it.hasNext()) {
                on = it.next();
                String dsInfo = on.getKeyProperty("name");
                // TODO - get lockPolicy
                String lockPolicy = null;
                dsInfo += ":lockPolicy=" + lockPolicy;
                dsInfo += ":minPoolSize=" + conn.getAttribute(on, "jdbcMinConnPool");
                dsInfo += ":maxPoolSize=" + conn.getAttribute(on, "jdbcMaxConnPool");
                dsInfo += ":maxOpenTime=" + conn.getAttribute(on, "jdbcMaxOpenTime");
                dsInfo += ":maxWaitTime=" + conn.getAttribute(on, "jdbcMaxWaitTime");
                dsInfo += ":maxWaiters=" + conn.getAttribute(on, "jdbcMaxWaiters");
                dsInfo += ":pstmtMax=" + conn.getAttribute(on, "jdbcPstmtMax");
                dsInfo += "\n";
                ret += dsInfo;
            }
        } else {
            ret += "No DataSources";
            ret += "\n";
        }
        return ret;
    }

    public static ArrayList<ObjectName> listJNDIResources(final String domain, final String server, final MBeanServerConnection conn) throws Exception {
        ArrayList<ObjectName> res = new ArrayList<ObjectName>();
        // Get JNDIResource MBean
        ObjectName ons = getJ2eeMBean(domain, server, "JNDIResource");
        Set onSet = conn.queryNames(ons, null);
        Iterator<ObjectName> it = onSet.iterator();
        while(it.hasNext()) {
            res.add(it.next());
        }
        return res;
    }

    public static String[] getTopics(final String domain, final String server, final MBeanServerConnection conn) throws Exception {
        ObjectName ons = getJonasServiceMBean(domain, server, "log");
        Set onSet = conn.queryNames(ons, null);
        if (onSet.isEmpty()) {
            return null;
        }
        ObjectName on = (ObjectName) onSet.iterator().next();
        //System.out.println(">> log MBean " + on);
        return (String[]) conn.getAttribute(on, "topics");
    }

    public static String getTopicLevel(final String domain, final String server, final MBeanServerConnection conn, final String topic) throws Exception {
        ObjectName ons = getJonasServiceMBean(domain, server, "log");
        Set onSet = conn.queryNames(ons, null);
        if (onSet.isEmpty()) {
            return null;
        }
        ObjectName on = (ObjectName) onSet.iterator().next();
        String operationName = "getTopicLevel";
        String[] params = {topic};
        String[] signature = {"java.lang.String"};
        return (String) conn.invoke(on, operationName, params, signature);
    }

    public static void setTopicLevel(final String domain, final String server, final MBeanServerConnection conn, final String topic, final String level) throws Exception {
        ObjectName ons = getJonasServiceMBean(domain, server, "log");
        Set onSet = conn.queryNames(ons, null);
        if (onSet.isEmpty()) {
            return;
        }
        ObjectName on = (ObjectName) onSet.iterator().next();
        String operationName = "setTopicLevel";
        String[] params = {topic, level};
        String[] signature = {"java.lang.String", "java.lang.String"};
        conn.invoke(on, operationName, params, signature);
    }
}
