/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008-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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.lib.management.extensions.base;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

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

import org.ow2.jonas.management.extensions.base.api.IBaseManagement;
import org.ow2.jonas.management.extensions.base.api.J2EEMBeanAttributeInfo;
import org.ow2.jonas.management.extensions.base.api.ManagementException;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * Base class for JOnAS management. This class is extended by classes
 * specialized for specific management aspects.
 * @author Adriana.Danes@bull.net
 * @author Oualaa Hani
 */
public class BaseManagement implements IBaseManagement {
    /**
     * create logger
     */
    protected static Log logger = LogFactory.getLog(BaseManagement.class);

    /**
     * Object used for synchronization.
     */
    protected static Object s_Synchro = new Object();

    /**
     * Current server name.
     */
    private String serverName = null;

    /**
     * Current domain name.
     */
    private String domainName = null;

    /**
     * Singleton instance of base management.
     */
    private static BaseManagement unique = null;

    /**
     * Base constructor.
     */
    public BaseManagement() {
        serverName = LocalManagementContext.getInstance().getServerName();
        domainName = LocalManagementContext.getInstance().getDomainName();
    }

    /**
     * Get singleton instance of base management.
     * @return
     */
    public static IBaseManagement getInstance() {
        if (unique == null) {
            unique = new BaseManagement();
        }
        return unique;
    }

    /**
     * @return The current server name.
     */
    public String getServerName() {
        return serverName;
    }

    /**
     * @return The current domain name.
     */
    public String getDomainName() {
        return domainName;
    }

    /**
     * Get the String attribute value of an MBean in the current MBean Server.
     * @param objectName The MBean's ObjectName
     * @param attrName The attribute name
     * @return The value of the attribute
     */
    public String getStringAttribute(final ObjectName objectName, final String attrName) throws ManagementException {
        String pServer = getServerName();
        String s = (String) JonasManagementRepr.getAttribute(objectName, attrName, pServer);
        return s;
    }

    /**
     * Get the integer attribute value of an MBean in the current MBean Server.
     * @param objectName The MBean's ObjectName
     * @param attrName The attribute name
     * @return The value of the attribute
     */
    public int getIntegerAttribute(final ObjectName objectName, final String attrName) throws ManagementException {
        String pServer = getServerName();
        Integer o = (Integer) JonasManagementRepr.getAttribute(objectName, attrName, pServer);
        return o.intValue();
    }

    /**
     * Return the value of a key property in an OBJECT_NAME.
     * @param objectName the OBJECT_NAME (String form)
     * @param keyName key property name
     * @return key value
     */
    public String getKeyValue(final String objectName, final String keyName) throws ManagementException {
        ObjectName on = null;
        try {
            on = ObjectName.getInstance(objectName);
        } catch (MalformedObjectNameException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        } catch (NullPointerException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
        return on.getKeyProperty(keyName);
    }

    /**
     * Return the values of a key property in String OBJECT_NAMEs.
     * @param objectNames the OBJECT_NAMEs
     * @param keyName key name
     * @return array of key values
     */
    public String[] getKeyValues(final String[] objectNames, final String keyName) throws ManagementException {
        ObjectName on = null;
        String[] keyValues = new String[objectNames.length];
        for (int i = 0; i < objectNames.length; i++) {
            String s_on = objectNames[i];
            try {
                on = ObjectName.getInstance(s_on);
            } catch (MalformedObjectNameException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                keyValues[i] = null;
            } catch (NullPointerException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                keyValues[i] = null;
            }
            keyValues[i] = on.getKeyProperty(keyName);
        }
        return keyValues;
    }

    /**
     * Get management attributes.
     * @param objectName the <code>ObjectName</code>
     * @param serverName server name.
     * @return attributes for the given objectName.
     * @throws ManagementException management operation failed
     */
    @SuppressWarnings("finally")
    public J2EEMBeanAttributeInfo[] getAttributes(final ObjectName objectName, final String serverName)
            throws ManagementException {
        List<J2EEMBeanAttributeInfo> ls = new ArrayList<J2EEMBeanAttributeInfo>();
        String[] str = null;
        try {
            MBeanInfo ret = JonasManagementRepr.getMBeanInfo(objectName, serverName);
            for (MBeanAttributeInfo info : ret.getAttributes()) {
                J2EEMBeanAttributeInfo modInfo = new J2EEMBeanAttributeInfo(info);
                Object value = JonasManagementRepr.getAttribute(objectName, modInfo.getName(), serverName);
                if ("[Ljava.lang.String;".equals(modInfo.getType())) {
                    List<String> l = new ArrayList<String>();
                    str = (String[]) JonasManagementRepr.getAttribute(objectName, modInfo.getName(), serverName);
                    for (String string : str) {
                        l.add(string);
                    }
                    value = l;
                } else {
                    value = JonasManagementRepr.getAttribute(objectName, modInfo.getName(), serverName).toString();
                }
                modInfo.setValue(value);
                ls.add(modInfo);
            }
        } catch (ManagementException e) {
            logger.debug("Management Exception in getAttributes", e);
        } catch (Exception e) {
            logger.debug("Exception in getAttributes", e);
            throw new ManagementException(e.getMessage());
        } finally {
            return ls.toArray(new J2EEMBeanAttributeInfo[ls.size()]);
        }

    }

    /**
     * Gets the value of a specific attribute of a named MBean.
     * @param on The ObjectName of the MBean.
     * @param attribute A String specifying the name of the attribute to be
     *        retrieved.
     * @param serverName The server name
     * @return The value of the attribute.
     * @throws ManagementException management operation failed
     */
    public Object getAttribute(final ObjectName on, final String attribute, final String serverName) throws ManagementException {
        return JonasManagementRepr.getAttribute(on, attribute, serverName);
    }

    /**
     * Implementation of the <code>isRegistered</code> method to be applied to a
     * server in the domain.
     * @return True if the MBean is already registered in the MBean server,
     *         false otherwise or if an exception is catched.
     * @param on ObjectName of the MBean we are looking for
     * @param serverName The server name
     * @throws ManagementException management operation failed
     */
    public boolean isRegistered(final ObjectName on, final String serverName) throws ManagementException {
        return JonasManagementRepr.isRegistered(on, serverName);
    }

    /**
     * Implementation of the <code>invoke</code> method to be applied to a
     * server in the domain.
     * @param on the ObjectName of the MBean that is the target of the invoke.
     * @param operation operation to invoke
     * @param param invoke parameters
     * @param signature invoke parameters signature
     * @param serverName The server's name
     * @return The object returned by the operation
     * @throws ManagementException management operation failed
     */
    public Object invoke(final ObjectName on, final String operation, final Object[] param, final String[] signature,
            final String serverName) throws ManagementException {
        return JonasManagementRepr.invoke(on, operation, param, signature, serverName);
    }

    /**
     * Return the list of <code>ObjectName</code> Mbean gotten by the query in
     * the current MbeanServer.
     * @param p_On Query Mbeans to search
     * @return The list of <code>ObjectName</code>
     * @throws ManagementException
     */
    public List getListMbean(final ObjectName p_On, final String serverName) throws ManagementException {
        synchronized (s_Synchro) {
            ArrayList<String> al = new ArrayList<String>();
            Iterator itNames = JonasManagementRepr.queryNames(p_On, serverName).iterator();
            while (itNames.hasNext()) {
                ObjectName item = (ObjectName) itNames.next();
                al.add(item.toString());
            }
            Collections.sort(al);
            return al;
        }
    }

    /**
     * Return the MBeanServer connection corresponding to a given server in the
     * current domain. The connection for servers in the domain are provided by
     * the DomainMonitor.
     * @param serverName The managed server name
     * @return The MBeanServerConnection corresponding to the managed server
     * @throws ManagementException Couldn't get the connection
     */
    public MBeanServerConnection getServerConnection(final String serverName) throws ManagementException {
        return JonasManagementRepr.getServerConnection(serverName);
    }

    /**
     * Return the MBeanServer connection corresponding to a current server in
     * the current domain. The connection for servers in the domain are provided
     * by the DomainMonitor.
     * @return The MBeanServerConnection corresponding to the managed server
     * @throws ManagementException Couldn't get the connection
     */
    public MBeanServerConnection getServerConnection() throws ManagementException {
        return JonasManagementRepr.getServerConnection(this.serverName);
    }

    /**
     * Sets the value of a specific attribute of a named MBean.
     * @param on The ObjectName of the MBean.
     * @param serverName The server name
     * @param attribute A String specifying the name of the attribute to be set.
     * @param value The value to set to the attribute.
     * @throws ManagementException management operation failed
     */
    public void setAttribute(final ObjectName on, final String attribute, final Object value, final String serverName)
            throws ManagementException {
        JonasManagementRepr.setAttribute(on, attribute, value, serverName);
    }

    /**
     * Sets the value of a specific attribute of a named MBean within the
     * current server.
     * @param on The ObjectName of the MBean.
     * @param attribute A String specifying the name of the attribute to be set.
     * @param value The value to set to the attribute.
     * @throws ManagementException management operation failed
     */
    public void setAttribute(final ObjectName on, final String attribute, final Object value) throws ManagementException {
        JonasManagementRepr.setAttribute(on, attribute, value, this.serverName);
    }

    /**
     * Get realm items of the given type.
     * @param realmType realm type.
     * @param sSecurityRealmUsed the used security realm.
     * @param domainName
     * @param serverName server's name
     * @return real items of the given type
     * @throws ManagementException any.
     */
    public List<RealmItem> getRealmItems(final String realmType, final String sSecurityRealmUsed, final String domainName, final String serverName) throws ManagementException {
        return getRealms(realmType, sSecurityRealmUsed, domainName, serverName);
    }

    /**
     * Get realm items of the given type within the current server of the current domain.
     * @param realmType realm type.
     * @param sSecurityRealmUsed the used security realm.
     * @return realm items of the given type
     * @throws ManagementException any.
     */
    public List<RealmItem> getRealmItems(final String realmType, final String sSecurityRealmUsed) throws ManagementException {
        return getRealms(realmType, sSecurityRealmUsed, this.domainName, this.serverName);
    }

    public List<RealmItem> getTomcatRealmItems(final String usedSecurityRealm, final String domainName, final String serverName) throws ManagementException {
        try {
            return AdminJmxHelper.getTomcatSecurityRealms(usedSecurityRealm, domainName, serverName);
        } catch (MalformedObjectNameException e) {
            throw new ManagementException(e.getMessage());
        }
    }

    public ObjectName getTomcatRealm(final String domainName, final String serverName)  throws ManagementException {
        try {
            return AdminJmxHelper.getTomcatSecurityRealm(domainName, serverName);
        } catch (Exception e) {
            throw new ManagementException(e.getMessage());
        }
    }

    /**
     * Sets the value of a specific attribute of a named MBean within the
     * current server.
     * @param on The ObjectName of the MBean.
     * @param attribute A String specifying the name of the attribute to be set.
     * @param value The value to set to the attribute.
     * @throws ManagementException management operation failed
     */
    private List<RealmItem> getRealms(final String realmType, final String sSecurityRealmUsed, final String domainName, final String serverName) {
        ArrayList<RealmItem> alRealms = new ArrayList<RealmItem>();
        try {
            // Detect the realm type
            if (realmType.equals("all")) {
                // all Realms
                addRealmItem(alRealms, "memory", sSecurityRealmUsed, domainName, serverName);
                addRealmItem(alRealms, "datasource", sSecurityRealmUsed, domainName, serverName);
                addRealmItem(alRealms, "ldap", sSecurityRealmUsed, domainName, serverName);
            }
            // Memory realm factories
            else if (realmType.equals("memory")) {
                addRealmItem(alRealms, "memory", sSecurityRealmUsed, domainName, serverName);
                // Force the node selected in tree
            }
            // Datasource realm factories
            else if (realmType.equals("datasource")) {
                addRealmItem(alRealms, "datasource", sSecurityRealmUsed, domainName, serverName);
                // Force the node selected in tree
            }
            // Ldap realm factories
            else if (realmType.equals("ldap")) {
                addRealmItem(alRealms, "ldap", sSecurityRealmUsed, domainName, serverName);
            }
            // Unknown
            else {
               throw new ManagementException("Unknown realm type" + realmType);
            }

        } catch (Exception e) {
            throw new ManagementException(e.getMessage());
        }
        Collections.sort(alRealms, new RealmItemByNameComparator());
        return alRealms;
    }

    /**
     * Add <code>RealmItem</code> in the list.
     *
     * @param p_Realms List to add found items
     * @param p_Type Type item to add (memory or datasource or ldap)
     * @param p_SecurityRealmUsed Name of realm used by the security service
     * or null if nothing used
     * @throws Exception
     */
    protected void addRealmItem(final ArrayList p_Realms, final String p_Type, final String p_SecurityRealmUsed, final String domainName, final String serverName)
        throws Exception {

        ArrayList al = null;

        // Memory realm factories
        if (p_Type.equals("memory")) {
            al = AdminJmxHelper.getSecurityMemoryFactories(domainName, serverName);
        }
        // Datasource realm factories
        else if (p_Type.equals("datasource")) {
            al = AdminJmxHelper.getSecurityDatasourceFactories(domainName, serverName);
        }
        // Ldap realm factories
        else if (p_Type.equals("ldap")) {
            al = AdminJmxHelper.getSecurityLdapFactories(domainName, serverName);
        }
        // Add
        if (al != null) {
            for (int i = 0; i < al.size(); i++) {
                if (p_SecurityRealmUsed != null) {
                    p_Realms.add(new RealmItem(al.get(i).toString(), p_Type
                        , p_SecurityRealmUsed.equals(al.get(i).toString())));
                } else {
                    p_Realms.add(new RealmItem(al.get(i).toString(), p_Type));
                }
            }
        }
    }
}
